動態代理:
目的:在不改變源代碼的情況下,對方法進行增強!
動態代理又分為兩種:
1.第一個就是基於接口的動態代理,他是由jdk提供的
2.基於子類的動態代理:cglib提供的:要想使用基於子類的動態代理:就必須得導入cglib的jar包
特性:被代理對象的任意方法
Spring_aop:總結
aop(Aspect Oriented Programming) :面向切面編程
切面:相對於業務主線的額外的功能模塊
在不改變程序源代碼的情況下對方法進行增強
aop的底層原理就是動態代理
aop的底層他會自動的根據代碼,選擇是使用基於還是使用子類的動態代理!
使用aop進行編程有啥好處呢?
1.簡化業務代碼
2.維護簡單
Aop相關術語
Joinpoint(連接點):執行的方法
pointcut(切入點):被增強的方法
Advice(通知/增強):就是額外的功能模塊
前置通知
后置通知
異常通知
最終通知
環繞通知
Aspect(切面):
對那個方法進行怎樣的增強
學習Aop重點關注的事情是啥呢?
1.重點關注的是一個一個的額外的功能模塊
2.aop的配置
第三 spring Aop的開發流程
定義一個切面類
普通的Java類,在這個類中通過一個一個的方法對目標對象進行增強
2.配置Aop
在xml里面配置文件中aop
i.將切面類交給spring管理
ii.聲明Aop配置
<aop:config>
iii.定義切入點
aop:pointcut
iiii.定義通知類型
<aop:aspect ref = "切面類的引用">
前置通知
<aop:before method="切面類的方法名" pointcut-ref="切入點表達式是引用"/>
后置通知<aop:afterRunturning method="切面類的方法名" pointcut-ref="切入點表達式是引用"/>
異常通知<aop:after-throwing method="切面類的方法名" pointcut-ref="切入點表達式是引用"/>
最終通知<aop:after method="切面類的方法名" pointcut-ref="切入點表達式是引用"/>
注意:異常通知只有出現了異常了 才會有通知
</aop:aspect>
基於注解的Aop配置(結合xml)
1.將切面類交給spring管理
2.使用注解的形式代替xml中的aop的配置
3.使用注解配置spring的ioc或者aop的時候都需要開啟對注解的支持!
注意:使用注解配置Aop和xml配置Aop在后置通知的時候,有一些差別!
注解的配置:是最后執行后置通知!
純注解配置Aop
1.聲明配置類
2.開啟對ioc的支持
3.開啟對aop注解的支持
@EnableAspectJAutoProxy
開發步驟以及代碼的解釋
<!-- 將切面類交給spring管理 -->
<bean id="logger" class="cn.itcast.utils.Logger"></bean>
<!-- 聲明AOP配置:需要使用aop的命名空間(必須導入aop的約束)
aop:config : 聲明aop配置
-->
<aop:config>
<!-- 定義切入點:定義被增強的方法
expression : execution(類的修飾符 返回值 包名.包名...類名.方法名(參數列表))
1.類的修飾符可以省略 :返回值 包名.包名...類名.方法名(參數列表)
2.返回值可以使用*號代替:標識任意返回值都可以
3.包名可以使用*號代替,一個包需要一個*
4.包名..代表此包 以及 此包的任意子包
5.類名可以使用*代替,代表任意類
6.方法名可以使用*代理,代表任意方法
7.參數類別可以使用..代表任意參數
**** 此寫法 * *..*.*(..) 不推薦
-->
<aop:pointcut expression="execution(* cn.itcast.service.impl..*.*(..))" id="pt"/>
<!-- 配置切面
aop:aspect
ref:被spring管理的切面類的引用
-->
<aop:aspect ref="logger">
<!-- 定義通知的類型
aop:before 定義前置通知
method:切面類中定義的方法名
pointcut-ref:切入點表達式是引用
-->
<aop:before method="before" pointcut-ref="pt"/>
這是xml里面的配置
注解配合xml使用代碼以及解釋
<!-- 開啟對spring ioc的支持 -->
<context:component-scan base-package="cn.itcast"></context:component-scan>
<!-- 開啟對aop注解的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
/*
* 切面類:
* 對目標對象進行日志部分的增強是通過此類中的方法來實現
* 在切面類上
* 定義切面類
* 在切面方法上
* 使用注解的形式定義通知類型
*/
@Component("logger")
@Aspect
public class Logger {//這是配置到類上的
/**
* 抽取切入點表達式
* @Pointcut:定義切入點表達式
* 這個注解需要配置到方法上,這個方法是一個空的方法
* 應用這個切入點表達式的時候,只需要引入方法名就可以了
* 但是需要 方法名() 需要加括號
*/
@Pointcut(value="execution(* cn.itcast.service.impl..*.*(..))")//注意:類名前面那兩個點改成一個點也是可以的
@Pointcut(value="execution(* cn.itcast.service.impl.*.add*(..))")//這是指定對那個方法進行增強
public void pt() {//定義一個通用的,這樣前置通知,后置通知等等用的時候只需要引用這個方法名就行了
}
/**
* 定義前置通知
* @Before :
* value : 可以是一個切入點表達式,也可以是一個切入點表達式的引用
* 切入點表達式
*/
@Before("pt()")
public void before() {
System.out.println("進入方法之前打印日志");
}
*這是注解配置xml的方法
純注解的配置方式
/*
* 切面類:
* 對目標對象進行日志部分的增強是通過此類中的方法來實現
* 在切面類上
* 定義切面類
* 在切面方法上
* 使用注解的形式定義通知類型
*/
@Component("logger")
@Aspect
public class Logger {
/**
* 抽取切入點表達式
* @Pointcut:定義切入點表達式
* 這個注解需要配置到方法上,這個方法是一個空的方法
* 應用這個切入點表達式的時候,只需要引入方法名就可以了
* 但是需要 方法名() 需要加括號
*/
@Pointcut(value="execution(* cn.itcast.service.impl..*.*(..))")
public void pt() {
}
/**
* 定義前置通知
* @Before :
* value : 可以是一個切入點表達式,也可以是一個切入點表達式的引用
* 切入點表達式
*/
@Before("pt()")
public void before() {
System.out.println("進入方法之前打印日志");
}
@AfterReturning("pt()")
public void afterReturning() {
System.out.println("執行目標方法得到返回值后打印日志");
}
@AfterThrowing("pt()")
public void afterThrowing() {
System.out.println("拋出異常打印日志");
}
@After("pt()")
public void after() {
System.out.println("在最終代碼塊中打印日志");
}
//5.定義環繞通知
/**
* 環繞通知是spring提供的一種
* 讓程序員手動指定要執行的增強方法的一種手段,
* 在此方法中可以根據需求自定義以上4中通知類型
* 在環繞通知中,需要手動去調用被增強的方法
* 借助spring提供的一個對象ProceedingJoinPoint
* 此對象中有一個方法,可以調用被增強的方法
* pjp.proceed();執行被增強的的方法
* eg:動態代理中的,method.invoke();
*
* @throws Throwable
*/
@Around("execution(* cn.itcast.service.impl..*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("前置通知");
Object obj = null;
try{
obj = pjp.proceed();
System.out.println("后置通知");
}catch(Exception e){
System.out.println("異常通知");
e.printStackTrace();
}finally{
System.out.println("最終通知");
}
return obj;
}
}
/spring的配置類
//在配置類中替換xml配置文件中的剩余配置項
/**
* 1。聲明配置類
* 2。開啟對ioc的支持
* 3。開啟對AOP注解的支持
* @EnableAspectJAutoProxy
*
*/
@Configuration
@ComponentScan(basePackages="cn.itcast")
@EnableAspectJAutoProxy
public class Config {
}
public class Client {
public static void main(String[] args) {
//測試ioc的配置
//ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
CustomerService customerService = (CustomerService)ac.getBean("customerService");
customerService.updateCustomer();
}
}
這是純注解的方式
Aop事物案例
@Component("logger")
@Aspect
public class Logger {
@Pointcut(value="execution(* cn.crm.service.impl..*.*(..))")
public void pt(){
}
@Around(value="pt()")
public Object tx(ProceedingJoinPoint pjp) throws Throwable{
Session session = null;
Transaction tx = null;
Object obj = null;
try {
session = HibernateUtils.getCurrentSession();
tx = session.beginTransaction();
obj = pjp.proceed();
tx.commit();
} catch (Exception e) {
System.out.println("出現異常了");
tx.rollback();
}finally {
System.out.println("最終執行的代碼");
}
return obj;
}
}
public class Client{
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
CustomerService customerService = (CustomerService)ac.getBean("customerService");
Customer customer = new Customer();
customer.setCustName("hello");
customerService.addCustomer(customer);
}
}
xml配置:
<context:component-scan base-package="cn.crm"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
這是一個小小的Aop的事物案例,當然Spring也提供了事物的支持,但是今天只做了個Aop的事物,希望對大家有幫助