1.增强一个类的三种方式
继承:
派生子类,重写方法
增强的对象不可以变,增强的功能可以变
装饰者模式:
FileOutStream
BufferedOutputStream
增强的对象可以变,增强的功能不变
/**
*@version 2020年7月22日下午3:19:34
*类说明:装饰者模式
*/
//面条
class Noodles {
public String getNoodles() {
return "noodles";
}
}
//加盐
class SaltNoodles extends Noodles{
Noodles noodles;
public SaltNoodles(Noodles noodles) {
super();
this.noodles = noodles;
}
public String getNoodles() {
return "salt" + noodles.getNoodles();
}
}
//加辣椒
class PepperNoodles extends Noodles{
Noodles noodles;
public PepperNoodles(Noodles noodles) {
super();
this.noodles = noodles;
}
public String getNoodles() {
return "pepper" + noodles.getNoodles();
}
}
public class NoodlesTest{
public static void main(String[] args) {
Noodles noodles = new Noodles();
System.out.println(noodles.getNoodles());
SaltNoodles saltNoodles = new SaltNoodles(noodles);
System.out.println(saltNoodles.getNoodles());
PepperNoodles pepperNoodles = new PepperNoodles(saltNoodles);
System.out.println(pepperNoodles.getNoodles());
}
}
动态代理:
增强的对象和增强的功能都可以变,是一种最灵活的增强方式
MyBatisMapper动态代理,AOP底层
接口:
public interface SomeService {
void doSome();
String doOther();
void test();
}
实现类:
public class SomeServiceImp implements SomeService{
@Override
public void doSome() {
System.out.println("doSome");
}
@Override
public String doOther() {
System.out.println("doOther");
return "doOther";
}
@Override
public void test() {
System.out.println("test");
}
}
动态代理测试类:
public class MyProxy {
/*
* 动态代理有两种实现:
* Proxy:JDK内置,要求目标类,必须有接口
* cglib:第三方jar包,有没有接口都可以增强
*/
public static void main(String[] args) {
//目标对象
SomeService someService= new SomeServiceImp();
//返回值是一个代理对象:目标对象增强以后的对象
/*
* ClassLoader loader:类加载器
* Class<?> interfaces:目标类的所有接口
* InvocationHandler h : 接口,需要用这个接口的实现类完成对目标类的增强
*/
SomeService obj = (SomeService) Proxy.newProxyInstance(someService.getClass().getClassLoader(), someService.getClass().getInterfaces(),
new InvocationHandler() {
/*Object proxy: 代理对象
* Method method:目标方法
* Object[] args : 目标方法的参数
*/
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
//切面--织入--前置通知
System.out.println("前置增强");
//调用目标方法
Object resoult = arg1.invoke(someService, arg2);
//切面--织入--后置通知
System.out.println("后置增强");
return resoult;
}
});
obj.doSome();
System.out.println("---------");
obj.doOther();
System.out.println("---------");
obj.test();
}
}
2. Spring搭建AOP环境
AOP是一种编程思想,很多框架都实现了AOP
Spring自己实现了AOP,使用麻烦:
IoC的基本jar+aop联盟包(官方组织,定义规范,aop接口)+aop.jar
AspectJ框架也实现,Spring官方也推荐使用AspectJ
IoC的基本jar+aopalliance.jar联盟包(官方组织,定义规范,aop接口)+aop.jar+aspectJ.jar+spring对apsectj整合包
3.五种通知-注解方式
添加约束头
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
<!-- 目标类对象 -->
<bean id="someServie" class="com.woniu.service.SomeServiceImp" />
<!-- 切面类对象 -->
<bean id="myAspect" class="com.woniu.service.MyAspect" />
<aop:aspectj-autoproxy />
</beans>
下面注解方式和配置方式都用到的接口和实现类及测试类
接口:
public interface SomeService {
void doSome();
String doOther();
void test();
}
实现类:
public class SomeServiceImp implements SomeService{
@Override
public void doSome() {
System.out.println("doSome");
}
@Override
public String doOther() {
System.out.println("doOther");
return "doOther";
}
@Override
public void test() {
System.out.println("test");
}
}
测试类:
public class SomeServiceImpTest {
@Test
public void testSomeService01() {
String path="com/woniu/service/applicationContext.xml";
//加载配置文件创建spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext(path);
//获取Bean
SomeService someService = (SomeService) ac.getBean("someServie");
someService.doSome();
System.out.println("--------");
System.out.println(someService.doOther());
System.out.println("--------");
someService.test();
}
}
前置通知
//定义切面类:将交叉业务方法,定义在切面类中
//这个注解告诉框架,当前类是切面类
@Aspect
public class MyAspect {
//前置通知:在目标方法执行之前,织入当前的交叉业务
//切入点表达式:满足什么条件的方法,会织入
@Before("execution(* *..SomeService.do*(..))")
public void before() {
System.out.println("before");
}
//所有的织入的方法都可以添加一个参数JoinPoint:切入点
@Before("execution(* *..SomeService.do*(..))")
public void before(JoinPoint jp) {
System.out.println("before,jp="+jp);
}
}
后置通知
//定义切面类:将交叉业务方法,定义在切面类中
//这个注解告诉框架,当前类是切面类
@Aspect
public class MyAspect {
//后置通知:在目标方法执行之后,织入当前的交叉业务
//切入点表达式:满足什么条件的方法,会织入
@AfterReturning("execution(* *..SomeService.do*(..))")
public void afterReturning() {
System.out.println("afterReturning");
}
//后置通知可以获取目标方法的返回值,但不能修改
@AfterReturning(value="execution(* *..SomeService.doOther(..))",returning="obj")
public void afterReturning(Object obj) {
System.out.println("afterReturning");
System.out.println("后置通知获取的返回值:" + obj.toString().toUpperCase());
}
}
环绕通知
//定义切面类:将交叉业务方法,定义在切面类中
//这个注解告诉框架,当前类是切面类
@Aspect
public class MyAspect {
//环绕通知:功能最强,可以替换其他四种通知的所有功能
//可以修改方法的返回值
@Around("execution(* *..SomeService.doOther(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
//通过ProceedingJoinPoint参数对象调用目标方法
Object object = pjp.proceed();
System.out.println("环绕后");
return object.toString().toUpperCase();
}
}
异常通知
//定义切面类:将交叉业务方法,定义在切面类中
//这个注解告诉框架,当前类是切面类
@Aspect
public class MyAspect {
//异常通知:目标方法发送异常以后执行
//切入点表达式:满足什么条件的方法,会织入
@AfterThrowing("execution(* *..SomeService.test(..))")
public void afterThrowing() {
System.out.println("异常通知");
}
@AfterThrowing(value="execution(* *..SomeService.test(..))",throwing = "ex")
public void afterThrowing(Exception ex) {
System.out.println("异常通知Exception:"+ex);
}
@AfterThrowing(value="execution(* *..SomeService.test(..))",throwing = "ex")
public void afterThrowing(ArithmeticException ex) {
System.out.println("异常通知ArithmeticException:"+ex);
}
}
异常通知的test()方法手动抛了个异常来测试
@Override
public void test() {
System.out.println("test");
if(true) {
throw new ArithmeticException("自定义异常");
}
}
最终通知
//定义切面类:将交叉业务方法,定义在切面类中
//这个注解告诉框架,当前类是切面类
@Aspect
public class MyAspect {
//最终通知:有没有异常多会执行
@After("execution(* *..SomeService.do*(..))")
public void after() {
System.out.println("最终通知");
}
}
4.五种通知-配置applicationContext.xml方式
前置通知
//切面类
public class MyAspect {
public void before() {
System.out.println("before");
}
public void before(JoinPoint jp) {
System.out.println("before,jp="+jp);
}
}
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
<!-- 目标类对象 -->
<bean id="someServie" class="com.woniu.service06.SomeServiceImp" />
<!-- 切面类对象 -->
<bean id="myAspect" class="com.woniu.service06.MyAspect" />
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* *..SomeService.doSome(..))" id="doSomePointcut"/>
<aop:pointcut expression="execution(* *..SomeService.doOther(..))" id="doOtherPointcut"/>
<aop:pointcut expression="execution(* *..SomeService.test(..))" id="testPointcut"/>
<!-- 配置切面织入 -->
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut="execution(* *..SomeService.doSome(..))"/>
<aop:before method="before" pointcut-ref="doOtherPointcut"/>
<aop:before method="before" pointcut-ref="testPointcut"/>
</aop:aspect>
</aop:config>
</beans>
后置通知
//切面类
public class MyAspect{
public void afterReturning() {
System.out.println("afterReturning");
}
public void afterReturning(Object obj) {
System.out.println("afterReturning");
System.out.println("后置通知获取的返回值:" + obj.toString().toUpperCase());
}
}
<!-- 目标类对象 -->
<bean id="someServie" class="com.woniu.service07.SomeServiceImp" />
<!-- 切面类对象 -->
<bean id="myAspect" class="com.woniu.service07.MyAspect" />
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* *..SomeService.doSome(..))" id="doSomePointcut"/>
<aop:pointcut expression="execution(* *..SomeService.doOther(..))" id="doOtherPointcut" />
<aop:pointcut expression="execution(* *..SomeService.test(..))" id="testPointcut"/>
<!-- 配置切面织入 -->
<aop:aspect ref="myAspect" >
<aop:after method="afterReturning" pointcut="execution(* *..SomeService.doSome(..))"/>
<aop:after-returning method="afterReturning(java.lang.Object)" pointcut-ref="doOtherPointcut" returning="obj"/>
</aop:aspect>
</aop:config>
环绕通知
//切面类
public class MyAspect {
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
//通过ProceedingJoinPoint参数对象调用目标方法
Object object = pjp.proceed();
System.out.println("环绕后");
return object.toString().toUpperCase();
}
}
<bean id="someServie" class="com.woniu.service08.SomeServiceImp" />
<!-- 切面类对象 -->
<bean id="myAspect" class="com.woniu.service08.MyAspect" />
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* *..SomeService.doSome(..))" id="doSomePointcut"/>
<aop:pointcut expression="execution(* *..SomeService.doOther(..))" id="doOtherPointcut" />
<aop:pointcut expression="execution(* *..SomeService.test(..))" id="testPointcut"/>
<!-- 配置切面织入 -->
<aop:aspect ref="myAspect" >
<aop:around method="around" pointcut-ref="doOtherPointcut" />
</aop:aspect>
</aop:config>
异常通知
//切面类
public class MyAspect {
public void afterThrowing() {
System.out.println("异常通知");
}
public void afterThrowing(Exception ex) {
System.out.println("异常通知Exception:"+ex);
}
public void afterThrowing(ArithmeticException ex) {
System.out.println("异常通知ArithmeticException:"+ex);
}
}
<!-- 目标类对象 -->
<bean id="someServie" class="com.woniu.service09.SomeServiceImp" />
<!-- 切面类对象 -->
<bean id="myAspect" class="com.woniu.service09.MyAspect" />
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* *..SomeService.doSome(..))" id="doSomePointcut"/>
<aop:pointcut expression="execution(* *..SomeService.doOther(..))" id="doOtherPointcut" />
<aop:pointcut expression="execution(* *..SomeService.test(..))" id="testPointcut"/>
<!-- 配置切面织入 -->
<aop:aspect ref="myAspect" >
<!-- <aop:after-throwing method="afterThrowing" pointcut-ref="doSomePointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="doOtherPointcut"/> -->
<aop:after-throwing method="afterThrowing(java.lang.Exception)" throwing="ex" pointcut-ref="testPointcut" />
</aop:aspect>
</aop:config>
最终通知
//切面类
public class MyAspect {
public void after() {
System.out.println("最终通知");
}
}
<!-- 目标类对象 -->
<bean id="someServie" class="com.woniu.service10.SomeServiceImp" />
<!-- 切面类对象 -->
<bean id="myAspect" class="com.woniu.service10.MyAspect" />
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* *..SomeService.doSome(..))" id="doSomePointcut"/>
<aop:pointcut expression="execution(* *..SomeService.doOther(..))" id="doOtherPointcut" />
<aop:pointcut expression="execution(* *..SomeService.test(..))" id="testPointcut"/>
<!-- 配置切面织入 -->
<aop:aspect ref="myAspect" >
<aop:after method="after" pointcut-ref="doSomePointcut"/>
</aop:aspect>
</aop:config>
类对象 -->
aop:config
<aop:pointcut expression=“execution(* …SomeService.doSome(…))" id=“doSomePointcut”/>
<aop:pointcut expression="execution( …SomeService.doOther(…))" id=“doOtherPointcut” />
<aop:pointcut expression="execution( *…SomeService.test(…))” id=“testPointcut”/>
<aop:aspect ref=“myAspect” >
<aop:after method=“after” pointcut-ref=“doSomePointcut”/>
</aop:aspect>
</aop:config>