Spring程序開(kāi)發(fā)步驟:
- 導(dǎo)入Spring開(kāi)發(fā)的基本包坐標(biāo)編寫
pom文件:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
- Dao接口和實(shí)現(xiàn)類
- 創(chuàng)建Spring核心配置文件
- 在Spring配置文件【applicationContext.xml】中配置UserDaolmpl
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl"></bean>
</beans>
- 使用Spring的API獲得 Bean 實(shí)例【調(diào)用的是Bean類的無(wú)參構(gòu)造方法創(chuàng)建對(duì)象】
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml"); 加載xml配置文件,創(chuàng)建Spring容器和bean標(biāo)簽中的對(duì)象
Userdao userDao = (Userdao) app.getBean("userDao"); 獲取容器中的對(duì)象
userDao.add();
配置文件【applicationContext.xml】bean標(biāo)簽的屬性
- scope:
| 取值范圍 | 說(shuō)明 |
|---|---|
| singleton | 默認(rèn)值,單例的 app.getBean()獲取幾次都獲取的是同一個(gè)對(duì)象 在創(chuàng)建Spring容器時(shí)對(duì)象也被創(chuàng)建了,并放入Spring容器中 |
| prototype | 多例的 ,app.getBean()獲取幾次獲取幾個(gè)不同的對(duì)象 每次getBean()時(shí)創(chuàng)建對(duì)象 |
| request | WEB項(xiàng)目中,Spring創(chuàng)建一個(gè)Bean的對(duì)象,將對(duì)象存入到request域中 |
| session | WEB 項(xiàng)目中,Spring創(chuàng)建一個(gè)Bean的對(duì)象,將對(duì)象存入到session域中 |
| global session | WEB項(xiàng)目中,應(yīng)用在Portlet 環(huán)境,如果沒(méi)有Portlet環(huán)境那么globalSession相當(dāng)global session |
- Bean生命周期配置
init-method:指定類中的初始化方法名稱
destroy-method:指定類中銷毀方法名稱
配置:
告訴spring初始化時(shí)執(zhí)行init方法,銷毀時(shí)執(zhí)行destroy方法
<bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
UserDaoImpl類:
package com.xjbt.spring.dao.impl;
import com.xjbt.spring.dao.Userdao;
public class UserDaoImpl implements Userdao {
@Override
public void add() {
}
public UserDaoImpl() {
System.out.println("無(wú)參構(gòu)造方法");
}
配置文件中已經(jīng)配置了該方法init-method="init"
public void init(){
System.out.println("初始化時(shí)執(zhí)行");
}
配置文件中已經(jīng)配置了該方法 destroy-method="destroy"
public void destroy(){
System.out.println("銷毀時(shí)執(zhí)行");
}
}
public class demo1 {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
獲取xml配置文件,然后會(huì)執(zhí)行這些方法,執(zhí)行順序:1執(zhí)行構(gòu)造方法,創(chuàng)建對(duì)象,2執(zhí)行初始化時(shí)方法init,3,執(zhí)行銷毀方法destroy
}
實(shí)例化對(duì)象的三種種配置方法:
1構(gòu)造方法實(shí)例化
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImp1""></bean>
2,工廠的靜態(tài)方法實(shí)例化
<bean id="userDao" class="com itheima.factory.StaticFactory" factory-method="getUserDao"></bean> 工廠類中的getUserDao方法時(shí)靜態(tài)方法
3,工廠實(shí)例方法實(shí)例化
<bean id="factory" class="com.itheima.factory.DynamicFactory"></bean> 找到工廠類,設(shè)置工廠類的id
通過(guò) factory-bean="factory"找到工廠類,factory-method="getUserDao"找到工廠類的方法創(chuàng)建userDao對(duì)象 getConnection時(shí)使用工廠方式創(chuàng)建Connection對(duì)象
<bean id="userDao" factory-bean="factory" factory-method="getUserDao">
Bean的依賴注入概念
等框架把持久層對(duì)象【dao】傳入業(yè)務(wù)層【service】,而不用我們自己去獲取。
依賴注入(Dependency Injection):它是Spring框架核心IOC的具體實(shí)現(xiàn)。
在編寫程序時(shí),通過(guò)控制反轉(zhuǎn),把對(duì)象的創(chuàng)建交給了Spring,但是代碼中不可能出現(xiàn)沒(méi)有依賴的情況。IOC解耦只是降低他們的依賴關(guān)系,但不會(huì)消除。例如:業(yè)務(wù)層仍會(huì)調(diào)用持久層的方法。
那這種業(yè)務(wù)層和持久層的依賴關(guān)系,在使用Spring之后,就讓Spring來(lái)維護(hù)了。
注入
主程序:調(diào)用service的save()方法
public class UserController {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
}
- set注入
set注入xml
<bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.xjbt.spring.service.impl.UserServiceImpl">
spring的set注入
<property name="userdao" ref="userDao"></property>這里的name是UserService類的setUserDao方法 ref是注入spring的id;
</bean>
使用set()方法注入
private Userdao userdao;
public void setUserdao(Userdao userdao) {
this.userdao = userdao;
}
@Override
public void save() {
沒(méi)有使用注入時(shí):獲取dao層
// ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
// Userdao userDao = (Userdao) app.getBean("userDao");
// userDao.save();*/
調(diào)用dao層的save()方法
userdao.save();
}
- 有參構(gòu)造方法注入
<!--構(gòu)造函數(shù)注入-->
<bean id="userService" class="com.xjbt.spring.service.impl.UserServiceImpl">
<constructor-arg name="userdao" ref="userDao"></constructor-arg>
</bean>
有參構(gòu)造注入
private Userdao userdao;
public UserServiceImpl(Userdao userdao){
this.userdao=userdao;
}
@Override
public void save() {
userdao.save();
}
Bean的依賴注入的數(shù)據(jù)類型
上面的操作,都是注入的引用Bean,除了對(duì)象的引用可以注入,普通數(shù)據(jù)類型,集合等都可以在容器中進(jìn)行注入。
- 普通數(shù)據(jù)類型
public class UserDaoImpl implements Userdao {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public void save() {
System.out.println(name+"----"+age);
System.out.println("dao層方法");
}
}
<bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
- 引用數(shù)據(jù)類型
public class UserDaoImpl implements Userdao {
private Map<String,User> mapUser; =>引用User
public void setMapUser(Map<String, User> mapUser) {
this.mapUser = mapUser;
}
}
<bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl">
<property name="mapUser">
<map>
<entry key="u1" value-ref="user1"></entry>
<entry key="u2" value-ref="user2"></entry>
</map>
</property>
</bean>
<bean id="user1" class="com.xjbt.spring.domain.User">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
<property name="addr" value="xj"></property>
</bean>
<bean id="user2" class="com.xjbt.spring.domain.User">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
<property name="addr" value="xj"></property>
</bean>
- 集合數(shù)據(jù)類型
public class UserDaoImpl implements Userdao {
private List listString;
private Properties propsUser;
public void setListString(List listString) {
this.listString = listString;
}
public void setPropsUser(Properties propsUser) {
this.propsUser = propsUser;
}
<bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl">
<property name="listString">
<list>
<value>zhangsan</value>
<value>lisi</value>
<value>wangwu</value>
</list>
</property>
<property name="propsUser">
<props>
<prop key="p1">pppp1</prop>
<prop key="p2">pppp2</prop>
<prop key="p3">pppp3</prop>
</props>
</property>
</bean>
引入其他配置文件(分模塊開(kāi)發(fā))
實(shí)際開(kāi)發(fā)中,Spring的配置內(nèi)容非常多,這就導(dǎo)致Spring配置很繁雜且體積很大,所以,可以將部分配置拆解到其他配置文件中,而在Spring主配置文件通過(guò)import標(biāo)簽進(jìn)行加載
<import resource="applicationContext-product.xml"></import>
<import resource="applicationContext-user.xml"></import>
Spring配置數(shù)據(jù)源
1.1數(shù)據(jù)源(連接池)的作用【數(shù)據(jù)源(連接池)是提高程序性能如出現(xiàn)的?!?br> 常見(jiàn)的數(shù)據(jù)源(連接池):DBCP、C3P0、BoneCP Druid等
- 事先實(shí)例化數(shù)據(jù)源
- 初始化部分連接資源
- 使用連接資源時(shí)從數(shù)據(jù)源中獲取
- 使用完畢后將連接資源歸還給數(shù)據(jù)源
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" =>加context標(biāo)簽
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" =>加context標(biāo)簽
>
<!--獲取jdbc配置文件-->
<context:property-placeholder location="classpath:jdbc.properties" /> =>獲取jdbc配置文件
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property> =>通過(guò)spring標(biāo)簽獲取jdbc.properties文件中的值
<property name="url" value="${jdbc_url}"></property>
<property name="username" value="${jdbc_username}"></property>
<property name="password" value="${jdbc_password}"></property>
</bean>
</beans>
/**
* Spring獲取dataSource數(shù)據(jù)源
* @throws Exception
*/
@Test
public void springDruidTest() throws Exception {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = app.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
Spring注解配置
Spring原始注解主要是替代<Bean>的配置
| 注解 | 說(shuō)明 |
|---|---|
| @Component | 使用在類上用于實(shí)例化bean |
| @Controller | 使用在web層類上用于實(shí)例化bean |
| @Service | 使用在service層類上用于實(shí)例化bean |
| @Repository | 使用在dao層上用于實(shí)例化bean |
| @Autowired | 使用在字段上用于根據(jù)類型依賴注入 【按照數(shù)據(jù)類型匹配對(duì)象 UserDao有多個(gè)實(shí)現(xiàn)類,需要加上下邊注解】 |
| @Qualifier | 結(jié)合@Autowired一起使用用于根據(jù)名稱依賴注入 按照id進(jìn)行匹配 |
| @Resource | 相當(dāng)于@Autowired+@Qualifier,按照名稱進(jìn)行注入 |
| @Value | 注入普通屬性 【 可以使用這個(gè)屬性使用spring表達(dá)式獲取xml文件中引入的jdbc.properties的內(nèi)容 @Value("${jdbc.driver}")賦值給標(biāo)注的屬性】 |
| @Scope | 標(biāo)注bean的作用范圍【單例/多列】 |
| @PostConstruct | 使用在方法上標(biāo)注該方法是bean的初始化方法 |
| @preDestory | 標(biāo)注該方法是bean的銷毀方法 |
在使用注解之前需要在xml配置文件中配置需要使用注解的掃描包<context:component-scan base-package="com.xjbt.Spring"/>
//<bean id="userService" class="com.xjbt.service.impl.UserServiceImpl">
//@Component("userService")
@Service("userService") =>實(shí)例化serviceBean
@Scope(singleton【獲取之前創(chuàng)建一次對(duì)象】|prototype【獲取一次創(chuàng)建一次對(duì)象】)
public class UserServiceImpl implements UserService {
// <property name="userDao" ref="userDao"></property>
@Autowired
@Qualifier("userDao")
@Resource(name="userDao")
private UserDao userDao;
使用注解可省略set方法
public void setUserDao(UserDao userdao) {
this.userDao = userdao;
}
@PostConstruct =>初始化時(shí)自動(dòng)執(zhí)行該方法
public public void init(){
}
@preDestory =>容器銷毀時(shí)自動(dòng)執(zhí)行該方法
public public void init(){
}
public void save() {
ApplicationContext app= new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userdao = app.getBean(UserDao.class);
userdao.save();
}
}
Spring新注解
| 注解 | 說(shuō)明 |
|---|---|
| @Configuration | 用于指定當(dāng)前類是一個(gè)Spring配置類,當(dāng)創(chuàng)建容器時(shí)會(huì)從該類上加載注解 |
| @ComponentScan | 用于指定Spring在初始化容器時(shí)要掃描的包。作用和在Spring 的xml配置文件中的<context:component-scan base-package="com.itheima"/>一樣 |
| @Bean | 使用在方法上,標(biāo)注將該方法的返回值存儲(chǔ)到Spring容器中 |
| @PropertySource | 用于加載.properties文件中的配置 |
| @Import | 用于導(dǎo)入其他配置類 |
@Configuration =>標(biāo)志該類是Spring的核心配置類
//<context:component-scan base-package="com.xjbt.Spring"/>
@ComponentScan("com.xjbt.Spring") =>掃描注解
//<context:property-placeholder location="classpath:jdbc.properties" />
@PropertySource("jdbc.properties") =>引入外部文件
@Import({DataSourceConfig.class}) =>導(dǎo)入其他配置
public class SpringConfig {
@Value("${jdbc.driver}") =>普通屬性賦值
private String driver;
@Value("${jdbc_url}")
private String url;
@Value("${jdbc_username}")
private String username;
@Value("${jdbc_password}")
private String password;
@Bean =>將方法的返回值放入spring容器中
public DruidDataSource getConnection() throws Exception {
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
ApplicationContext app =new ClassPathXmlApplicationContext("applicationContext.xml");=>根據(jù)配置文件中獲取對(duì)象
ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfig.class); =》根據(jù)配置類獲取對(duì)象
Spring集成junnit
@RunWith(SpringJUnit4ClassRurner.class)
//@ContextConfiguration(classes = {value="applicationContext.xml")
@ContextConfiguration(classes = {SpringCofiguration. class})
public class SpringJunitTest {
@Autowired
private UserService userService;
@Autowired
private DataSource dataSource;
@Test
public void test1() throws SQLException {
userService.save() ;
System out.print1n(dataSource.getConnection() ) ;
}
ApplicationContext應(yīng)用上下文獲取方式【了解】
如果有多個(gè)Servlet獲取Spring中創(chuàng)建的對(duì)象,就要寫多次 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");來(lái)訪問(wèn)配置文件。顯然這樣是不合理的。
應(yīng)用上下文對(duì)象是通過(guò)方式獲取的,但是每次從容器中獲得Bean時(shí)都要編寫new ClasspathXmIApplicationContext(spring配置文件),這樣的弊端是配置文件加載多次,應(yīng)用上下文對(duì)象創(chuàng)建多次。
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = app.getBean(UserService.class);
userService.save();
}
}
在在Web項(xiàng)目中,可以使用ServletContextListener監(jiān)聽(tīng)Web應(yīng)用的啟動(dòng),我們可以在Web應(yīng)用啟動(dòng)時(shí),就加載Spring的配置文件,創(chuàng)建應(yīng)用上下文對(duì)象ApplicationContext,在將其存儲(chǔ)到最大的域servletContext域中,這樣就可以在任意位置從域中獲得應(yīng)用上下文ApplicationContext對(duì)象了。
1.web.xml中配置監(jiān)聽(tīng)對(duì)象。
<!--配置監(jiān)聽(tīng)器-->
<listener>
<listener-class>com.xjbt.spring_MVC.listener.ContextLoaderListener</listener-class>
</listener>
2,創(chuàng)建監(jiān)聽(tīng)對(duì)象:
public class ContextLoaderListener implements ServletContextListener { =>實(shí)現(xiàn)監(jiān)聽(tīng)接口
public void contextInitialized(ServletContextEvent servletContextEvent) { =>監(jiān)聽(tīng)服務(wù)啟動(dòng)時(shí)
ServletContext servletContext = servletContextEvent.getServletContext(); =>獲取servletContext全局對(duì)象
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml"); =>獲取Spring容器對(duì)象以及在applicationContext.xml中配置的Bean對(duì)象
servletContext.setAttribute("app",app) =>將Spring容器存入Servlet全局對(duì)象 servletContext
}
public void contextDestroyed(ServletContextEvent servletContextEvent) { =>監(jiān)聽(tīng)服務(wù)結(jié)束時(shí)
}
}
3servlet中獲取
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = req.getServletContext(); =>獲取全局對(duì)象
ApplicationContext app=(ApplicationContext)servletContex.getAttribute("app") =>獲取app
UserService userService = app.getBean(UserService.class);
userService.save();
}
}
優(yōu)化代碼
- 監(jiān)聽(tīng)對(duì)象
public class ContextLoaderListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
********存在問(wèn)題:如果修改了applicationContext.xml文件名稱,那么必須動(dòng)監(jiān)聽(tīng)對(duì)象********
servletContext.setAttribute("app",app)
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
解決辦法:
將applicationContext.xml配置到web.xml文件中
設(shè)置全局初始化參數(shù)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
修改監(jiān)聽(tīng)對(duì)象
public class ContextLoaderListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation"); 通過(guò)全局對(duì)象獲取全局初始化參數(shù)
ApplicationContext app=new ClassPathXmlApplicationContext(contextConfigLocation);
servletContext.setAttribute("app",app)
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
- servlet
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = req.getServletContext();
ApplicationContext app=(ApplicationContext)servletContex.getAttribute("app")
********存在問(wèn)題:屬性名稱耦合度高********
UserService userService = app.getBean(UserService.class);
userService.save();
}
}
解決辦法:創(chuàng)建工具
public class WebApplicationUtils {
public static ApplicationContext getApplication(ServletContext servletContext){
return (ApplicationContext) servletContext.getAttribute("app");
}
}
servlet
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
ServletContext servletContext = req.getServletContext();
//ApplicationContext app = (ApplicationContext)servletContext.getAttribute("app");
ApplicationContext app= WebApplicationUtils.getApplication(servletContext);
*************************** getApplication是靜態(tài)方法,所以不用創(chuàng)建WebApplicationUtils對(duì)象直接使用這個(gè)方法。*****************
UserService userService = app.getBean(UserService.class);
userService.save();
}
}
spring使用數(shù)據(jù)源
- 導(dǎo)包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
- web.xml配置
<!--設(shè)置全局初始化參數(shù)-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:path:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- servlet使用
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = req.getServletContext();
WebApplicationContext app= WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService userService = app.getBean(UserService.class);
userService.save();
}
}
AOP
是通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。
AOP為Aspect Oriented Programming的縮寫,意思為面向切面編程,是通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。
AOP是OOP的延續(xù),是軟件開(kāi)發(fā)中的一個(gè)熱點(diǎn),也是Spring框架中的一個(gè)重要內(nèi)容,是函數(shù)式編程的一種衍生范型。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率。
- 作用:在程序運(yùn)行期間,在不修改源碼的情況下對(duì)方法(save())進(jìn)行功能增強(qiáng)(日志控制)
- 優(yōu)勢(shì):減少重復(fù)代碼,提高開(kāi)發(fā)效率,并且便于維護(hù)
AOP的底層實(shí)現(xiàn)
SpringMVC運(yùn)行期間生成代理對(duì)象,代理對(duì)象的方法先執(zhí)行增強(qiáng)功能,在調(diào)用目標(biāo)對(duì)象的方法,完成對(duì)象方法功能增強(qiáng)。
實(shí)際上,AOP的底層是通過(guò)Spring提供的的動(dòng)態(tài)代理技術(shù)實(shí)現(xiàn)的。在運(yùn)行期間,Spring通過(guò)動(dòng)態(tài)代理技術(shù)動(dòng)態(tài)的生成代理對(duì)象,代理對(duì)象方法執(zhí)行時(shí)進(jìn)行增強(qiáng)功能的介入,在去調(diào)用目標(biāo)對(duì)象的方法,從而完成功能的增強(qiáng)。
常用的動(dòng)態(tài)代理技術(shù)
JDK代理:基于接口的動(dòng)態(tài)代理技術(shù)
cglib代理:基于父類的動(dòng)態(tài)代理技術(shù)
如果目標(biāo)對(duì)象有接口:jdk通過(guò)目標(biāo)對(duì)象的接口動(dòng)態(tài)生成代理對(duì)象,
如果目標(biāo)對(duì)象沒(méi)有接口:通過(guò)cglib給目標(biāo)對(duì)象動(dòng)態(tài)生成一個(gè)子對(duì)象,這個(gè)對(duì)象就是代理對(duì)象。

- jdk動(dòng)態(tài)代理:
public class AdviceTest {
public static void main(String[] args) {
//目標(biāo)對(duì)象
final Target target=new Target();
//增強(qiáng)對(duì)象
final ProxyAdvice advice=new ProxyAdvice();
//jdk動(dòng)態(tài)代理對(duì)象
TargetInterface proxy=(TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(),//目標(biāo)對(duì)象類加載器
target.getClass().getInterfaces(),//目標(biāo)對(duì)象相同的接口字節(jié)碼數(shù)組
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
advice.before();//增強(qiáng)方法
Object invoke=method.invoke(target,args);//執(zhí)行目標(biāo)方法 通過(guò)這個(gè)方法就目標(biāo)方法已被動(dòng)態(tài)代理
advice.after();//增強(qiáng)方法
return invoke;
}
}
);
proxy.save();
}
}
- cglib動(dòng)態(tài)代理
public class AdviceTest {
public static void main(String[] args) {
//目標(biāo)對(duì)象
final Target target=new Target();
//增強(qiáng)對(duì)象
final ProxyAdvice advice=new ProxyAdvice();
//cglib動(dòng)態(tài)代理對(duì)象
//1,創(chuàng)建增強(qiáng)器
Enhancer enhancer=new Enhancer();
//2,設(shè)置父類(目標(biāo)對(duì)象)
enhancer.setSuperclass(target.getClass());
//3,設(shè)置回調(diào)
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
advice.before();
Object invoke = method.invoke(target, args);
advice.after();
return invoke;
}
});
//4,創(chuàng)建代理對(duì)象
Target proxy =(Target) enhancer.create();
proxy.save();
}
Spring封裝的AOP
- AOP相關(guān)概念
Spring的AOP實(shí)現(xiàn)底層就是對(duì)上面的動(dòng)態(tài)代理的代碼進(jìn)行了封裝,封裝后我們只需要對(duì)需要關(guān)注的部分進(jìn)行代碼編寫,并通過(guò)配置的方式完成指定目標(biāo)的方法增強(qiáng)。
-
常用的術(shù)語(yǔ)如下:
Target (目標(biāo)對(duì)象):代理的目標(biāo)對(duì)象
Proxy(代理):一個(gè)類被AOP織入增強(qiáng)后,就產(chǎn)生一個(gè)結(jié)果代理類
Joinpoint(連接點(diǎn)):所謂連接點(diǎn)是指那些被攔截到的點(diǎn)。在spring中,這些點(diǎn)指的是方法,因?yàn)閟pring只支持方法類型的連接點(diǎn)【目標(biāo)對(duì)象的方法】
Pointcut(切入點(diǎn)):所謂切入點(diǎn)是指我們要對(duì)哪些Joinpoint進(jìn)行攔截的定義【目標(biāo)對(duì)象要增強(qiáng)的方法】
Advice(通知/增強(qiáng))︰所謂通知是指攔截到Joinpoint之后所要做的事情就是【增強(qiáng)方法】
Aspect(切面):是切入點(diǎn)和通知(引介)的結(jié)合【目標(biāo)方法加入增強(qiáng)方法】
Weaving (織入):是指把增強(qiáng)應(yīng)用到目標(biāo)對(duì)象來(lái)創(chuàng)建新的代理對(duì)象的過(guò)程。spring采用動(dòng)態(tài)代理織入,而AspectJ采用編譯期織入和類裝載期織入 - 需要編寫的內(nèi)容
- 編寫核心業(yè)務(wù)代碼(目標(biāo)類的目標(biāo)方法)
- 編寫切面類,切面類中有通知(增強(qiáng)功能方法)
- 在配置文件中,配置織入關(guān)系,即將哪些通知與哪些連接點(diǎn)進(jìn)行結(jié)合
- AOP技術(shù)實(shí)現(xiàn)的內(nèi)容
spring框架監(jiān)測(cè)需要增強(qiáng)的方法【切入點(diǎn)】執(zhí)行時(shí),spring框架創(chuàng)建代理對(duì)象,對(duì)需要增強(qiáng)的方法進(jìn)行增強(qiáng),完成代碼運(yùn)行。
Spring框架監(jiān)控切入點(diǎn)方法的執(zhí)行。一旦監(jiān)控到切入點(diǎn)方法被運(yùn)行,使用代理機(jī)制,動(dòng)態(tài)創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象,根據(jù)通知類別,在代理對(duì)象的對(duì)應(yīng)位置,將通知對(duì)應(yīng)的功能織入,完成完整的代碼邏輯運(yùn)行。
- AOP底層使用哪種代理方式
在spring中,框架會(huì)根據(jù)目標(biāo)類是否實(shí)現(xiàn)了接口來(lái)決定采用哪種動(dòng)態(tài)代理的方式。
快速入門基于application.xml
- 導(dǎo)入AOP相關(guān)坐標(biāo)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
- 創(chuàng)建目標(biāo)接口和目標(biāo)類(內(nèi)部有切點(diǎn))
- 創(chuàng)建切面類(內(nèi)部有增強(qiáng)方法)
public class MyAspect {
public void before(){
System. out.pr int1n("前置增強(qiáng)...... ....");
}
public void afterReturning() {
System. out.print1n("后置增強(qiáng)..........");
)
/ /Proceeding JoinPoint:正在執(zhí)行的連接點(diǎn)===切點(diǎn)
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System. out.print1n("環(huán)繞前增強(qiáng)....");
0bject proceed = pjp.proceed() ; / /切點(diǎn)方法System. out.println("環(huán)繞后增強(qiáng)....") ;return proceed;
public void afterThrowing(){
System. out.println("異常拋出增強(qiáng)......")
)
public void after(){
System. out.println("最終增強(qiáng).....");
)
- 將目標(biāo)類和切面類的對(duì)象創(chuàng)建權(quán)交給spring
- 在applicationContext.xml中配置織入關(guān)系
<!--目標(biāo)對(duì)象-->
<bean id="target" class="com.xjbt.proxy.aop.Target"></bean>
<!--切面對(duì)象-->
<bean id="myAspect" class="com.xjbt.proxy.aop.MyAspect"></bean>
<!--織入配置-->
<aop:config>
<!--聲明切面-->
<aop:aspect ref="myAspect">
<!--<aop:before method="before" pointcut="execution(public void com.xjbt.proxy.aop.Target.save())"></aop:before>-->
<aop:before method="before" pointcut="execution(* com.xjbt.proxy.aop.*.*(..))"></aop:before>
pointcut=execution =>修飾符省略 (*)任意返回值.(com.xjbt.proxy.aop)包名.(*)類名.*(..)(任意參數(shù)的任意方法名)
method=>
</aop:aspect>
</aop:config>

5.1抽取表達(dá)式
<aop:config>
<!--聲明切面-->
<aop:aspect ref="myAspect">
<!--抽取切點(diǎn)表達(dá)式-->
<aop:pointcut id="myPointcut" expression="execution(* com.xjbt.proxy.aop.*.*(..))"></aop:pointcut><!--切面:切點(diǎn)通知-->
<aop:around method="after" pointcut-ref="myPointcut"/>
<aop:after method="after" pointcut-ref="myPointcut" />
</aop:aspect>
</aop:config>
- 測(cè)試代碼
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AOPtest {
@Autowired
private TargetInterface target;
@Test
public void test1(){
target.save();
}
}
快速入門基于注解
- 創(chuàng)建目標(biāo)類(內(nèi)部有切點(diǎn))
@Component("target")
public class Target{
public void save() {
System.out.println("目標(biāo)方法執(zhí)行");
}
}
- 創(chuàng)建切面類(內(nèi)部有增強(qiáng)方法)
@Component("myAspect") //注入到spring中
@Aspect //表示這是一個(gè)前面類
public class MyAspect {
@Before("execution(* com.xjbt.proxy.anno.*.*(..))") //配置前置通知
public void before(){
System.out.println("動(dòng)態(tài)代理前置對(duì)象");
}
}
- 將目標(biāo)類和切面類的對(duì)象創(chuàng)建權(quán)交給spring
- 在切面類中使用注解配置織入關(guān)系
- 在配置文件中開(kāi)啟組件掃描和AOP的自動(dòng)代理
<!--注解掃描包 識(shí)別放入spring容器的類創(chuàng)建對(duì)象-->
<context:component-scan base-package="com.xjbt.proxy.anno"/>
<!--使用aop注解 -->
<aop:aspectj-autoproxy />
- 測(cè)試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AOPtest {
@Autowired
private Target target;
@Test
public void test1(){
target.save();
}
}
spring事務(wù)控制
1. 編程式事務(wù)管理器相關(guān)對(duì)象
- PlatformTransactionManager 平臺(tái)【Platform】事務(wù)控制器【TransactionManager】:spring的事務(wù)管理器
| PlatformTransactionManager方法 | 說(shuō)明 |
|---|---|
| Transactionstatus getTransaction (TransactionDefination defination) | 獲取事務(wù)的狀態(tài)信息 |
| void commit (Transactionstaxus status) | 提交事務(wù) |
| void rollback(Transactionstatus status) | 回滾事務(wù) |
PlatformTransactionManager是接口類型,不同的 Dao層技術(shù)則有不同的實(shí)現(xiàn)類
例如:
Dao層技術(shù)是jdbc或mybatis 時(shí):org.springframework.jdbc.datasource.DatasourceTransactionManager
Dao層技術(shù)是hibernate時(shí):org.springframework.orm.hibernate5.HibernateTransactionManager
- TransactionDefinition事務(wù)的定義信息對(duì)象:
| 方法 | 說(shuō)明 |
|---|---|
| int getIsolationLevel() | 獲得事務(wù)的隔離級(jí)別 |
| int getPropogationBehavior() | 獲得事務(wù)的傳播行為 |
| int getTimeout() | 獲得超時(shí)時(shí)間 |
| boolean isReadonly() | 是否只讀 |
1.事務(wù)隔離級(jí)別
設(shè)置隔離級(jí)別,可以解決事務(wù)并發(fā)產(chǎn)生的問(wèn)題,如臟讀、不可重復(fù)讀和虛讀。
ISOLATION_DEFAULT 【默認(rèn)】
ISOLATION_READ_UNCOMMITTED 【讀不可提交】
ISOLATION_READ_COMMITTED 【讀并提交】
ISOLATION_REPEATABLE_READ 【可重復(fù)讀】
ISOIATION_SERIALIZABLE 【串行化 全能解決性能底】
2.事務(wù)傳播行為【解決調(diào)用業(yè)務(wù)方法時(shí)事務(wù)統(tǒng)一性的問(wèn)題】
a業(yè)務(wù)方法調(diào)用b業(yè)務(wù)方法,都有事務(wù)控制,會(huì)出現(xiàn)重復(fù)或統(tǒng)一的問(wèn)題
REQUIRED:如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù),如果已經(jīng)存在一個(gè)事務(wù)中,加入到這個(gè)事務(wù)中。一般的選擇(默認(rèn)值)
SUPPORTS:支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就以非事務(wù)方式執(zhí)行(沒(méi)有事務(wù))
MANDATORY:使用當(dāng)前的事務(wù),如果當(dāng)前沒(méi)有事務(wù),就拋出異常
REQUERS_NE新建事務(wù),如果當(dāng)前在事務(wù)中,把當(dāng)前事務(wù)掛起。
NOT SUPPORTED:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起
NEVER:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),拋出異常
NESTED:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒(méi)有事務(wù),則執(zhí)行REQUIRED類似的操作
超時(shí)時(shí)間:默認(rèn)值是-1,沒(méi)有超時(shí)限制。如果有,以秒為單位進(jìn)行設(shè)置
是否只讀:建議查詢時(shí)設(shè)置為只讀
-
TransactionStatus:事務(wù)具體的運(yùn)行狀態(tài)
隨著程序的運(yùn)行實(shí)時(shí)變化的不需要配置。
| 方法 | 說(shuō)明 |
|---|---|
| boolean hassavepoint() | 是否存儲(chǔ)回滾點(diǎn) |
| boolean iscompleted() | 事務(wù)是否完成 |
| boolean isNewTransaction() | 是否是新事務(wù) |
| boolean isRollbackonly() | 事務(wù)是否回滾 |
2. 聲明式事務(wù)控制
- spring配置文件中配置聲明
事務(wù)管理是屬于系統(tǒng)層面的服務(wù),而不是業(yè)務(wù)邏輯的一部分,如果想要改變事務(wù)管理策劃的話也只需要在定義文件中重新配置即可。
在不需要事務(wù)管理的時(shí)候,只要在設(shè)定文件上修改一下,即可移去事務(wù)管理服務(wù),無(wú)需改變代碼重新編譯,這樣維護(hù)起來(lái)極其方便。
注意: Spring聲明式事務(wù)控制底層就是AOP。
快速入門
<!--配置切點(diǎn)-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!--配置事務(wù)管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事務(wù)管理器 增強(qiáng)方法-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--設(shè)置事務(wù)屬性-->
<tx:attributes>
<tx:method name="advice" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1" />
<tx:method name="*"/> <!--任意方法-->
</tx:attributes>
</tx:advice>
<!--AOP織入-->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))" />
</aop:config>
設(shè)置事務(wù)屬性的解釋
name:切點(diǎn)方法名稱
isolation:事務(wù)的隔離級(jí)別
propogation:事務(wù)的傳播行為
timeout:超時(shí)時(shí)間
read-only:是否只讀
-
spring注解事務(wù)控制
自定義類可使用注解,非自定義的配置到xml中。
xml配置文件
<!--組件掃描-->
<context:component-scan base-package="com.itheima" />
<!--配置事務(wù)管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--使用事務(wù)控制注解-->
<tx:annotation-driven transaction-manager="transactionManager" />
AccountService.java
@Service("accountService")
@Transactional(isolation = Isolation.DEFAULT)//所有類下的方法控制事務(wù)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)//就近原則
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
accountDao.in(inMan,money);
}
}
AccountDao.java
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void out(String outMan, double money) {
jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
}
public void in(String inMan, double money) {
jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
}
}
AccountController.java測(cè)試
public class AccountController {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = app.getBean(AccountService.class);
//int i=1/0;
accountService.transfer("tom","lucy",500);
}
}
①使用@Transactional在需要進(jìn)行事務(wù)控制的類或是方法上修飾,注解可用的屬性同xml配置方式,例如:隔離級(jí)別、傳播行為等。
②注解使用在類上,那么該類下的所有方法都使用同一套注解參數(shù)配置。使用在方法上,不同的方法可以采用不同的事務(wù)參數(shù)配置。
③Xml配置文件中要開(kāi)啟事務(wù)的注解驅(qū)動(dòng)<tx : annotation-driven />
