AOP编程
AOP编程
1、AOP的概念
AOP (Aspect Oriented Programing) —— 面向切面编程 = Spring动态代理开发
-
以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建
-
切面 = 切入点 + 额外功能
OOP (Object Oritened Programing)【面向对象编程Java】
- 以对象为基本单位的程序开发,通过对象间的彼此协同,相互调用,完成程序的构建
POP (Producer Oriented Programing) 【面向过程(方法、 函数)编程C】
- 以过程为基本单位的程序开发,通过过程间的彼此协同,相互调用,完成程序的构建
AOP的概念:
本质就是Spring得动态代理开发,通过代理类为原始类增加额外功能。
好处:利于原始类的维护
注意:AOP编程不可能取代OOP ,是OOP编程的有益补充。
2、AOP编程的开发步骤
- 原始对象
- 额外功能(MethodInterceptor)
- 切入点
- 组装切面( 额外功能+切入点)
3、切面的名词解释
切面 = 切入点 + 额外功能
几何学:面 = 点 + 相同的性质
动态代理类的创建
1、JDK的动态代理
-
Proxy.newProxyInstance 方法参数详解
-
编码
public class TestJDKProxy { /** * 用于测试:测试JDK代理对象 * */ @Test public void test(){ /* 1.借用类加载器 TestJDKProxy UserServiceImpl 2. JDK8.0前 final UserService userService = new UserServiceImpl(); */ // 创建原始对象 UserService userService = new UserServiceImpl(); // JDK创建动态代理 // InvocationHandler hander = new InvocationHandler() { // @Override // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ...... // } // }; InvocationHandler hander = (proxy, method, args) -> { System.out.println("---log---"); Object ret = method.invoke(userService, args); return ret; }; UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), hander); userServiceProxy.login("你好", "123456"); userServiceProxy.register(new User()); } }
2、CGlib的动态代理
CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,即生成子类。这样既可以保证两者方法一致, 又能在代理类中提供新的实现(额外功能+原始方法)
public class TestCglibProxy {
public static void main(String[] args) {
// 创建原始对象
UserService userService = new UserService();
/*
2通过cglib方式创建动态代理对象
Proxy.newProxyInstance(classloader, interface, invocationHandler)
对比:
Enhancer.setClassLoader()
Enhancer.setSuperClass()
Enhancer.setCallback(); ---> MethodInterceptor(cglib)
Enhancer.create() ---> 代理
*/
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCglibProxy.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("---log---");
Object invoke = method.invoke(userService, args);
return invoke;
}
};
enhancer.setCallback(interceptor);
UserService userService1Proxy = (UserService) enhancer.create();
userService1Proxy.login("我","45");
userService.register(new User());
}
}
总结
- JDK动态代理
Proxy.newproxyInstance()
通过接口创建代理的实现类 Cglib
动态代理Enhancer
通过继承父类创建的代理类- JDK和CGLIB动态代理区别
4、Spring工厂如何加工原始对象
-
思路分析
-
编码
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { InvocationHandler hander = (proxy, method, args) -> { System.out.println("---log---"); Object invoke = method.invoke(bean, args); return null; }; return Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(),bean.getClass().getInterfaces(),hander); } }
<bean id="userService" class="com.company.factory.UserServiceImpl"/> <!-- 1. 实现BeanPostProcessor进行加工 2.配置文件中对BeanPostProcessor进行配置 --> <bean id="myBeanPostProcessor" class="com.company.factory.MyBeanPostProcessor"/>
基于注解的AOP编程
1、基于注解的AOP编程的开发步骤
-
原始对象
-
额外功能
-
切入点
-
组装切面
#通过切面类 定义了 额外功能 @Around 定义了 切入点 @Around("execution(* login(..))") @Aspect切面类 package com.company.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; /* 之前: 1.额外功能 public class MyAround implements MethodInterceptor{ public Object invoke(MethodInvocation invocation){ Object ret = invocation.proceed(); return ret; } } 2.切入点 <aop:config <aop:pointcut id="" expression="execution(* login(..))"/> */ @Aspect public class MyAspect { @Around("execution(* login(..))") public Object around(ProceedingJoinPoint joinPoint){ System.out.println("--- aspect log ---"); Object ret = null; try { ret = joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } return ret; } }
<bean id="userService" class="com.company.aspect.UserServiceImpl"/> <!-- 切面 1.额外功能 2.切入点 3.组装切面 --> <bean id="aspect" class="com.company.aspect.MyAspect"/> <!--通知Spring基于注解进行AOP编程--> <aop:aspectj-autoproxy/>
2、细节
1. 切入点复用
切入点复用:在切面类中定义一个函数上面@Pointcut注解 通过这种方式,定义切入点表达式,后续更加有利于切入点复用。
@Aspect
public class MyAspect {
@Pointcut("execution(* login(..))")
public void myPointCut() {
}
@Around(value = "myPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("--- aspect log ---");
Object ret = null;
ret = joinPoint.proceed();
return ret;
}
@Around(value = "myPointCut()")
public Object around1(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("--- aspect log ---");
Object ret = null;
ret = joinPoint.proceed();
return ret;
}
}
2. 动态代理的创建方式
AOP底层实现
2种代理创建方式
1.JDK 通过实现接口做新的实现类方式创建代理对象
2.Cglib 通过继承父类 做新的子类
创建代理对象
默认情况AOP编程底层应用JDK动态代理创建方式
<!-- 如果切换Cglib -->
1.基于注解AOP开发
<aop: aspectj-autoproxy proxy-target-class="true" />
2.传统的AOP开发
<aop :config proxy-target-class="true"></aop>
AOP开发中的注意点
在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过普通的方式调用,都调用的是原始方法)。如果想让内层的方法也调用代理对象的方法,通过实现ApplicationAware
接口进而实现AppicationContextAware
方法获得工厂,进而获得代理对象
@Pointcut("execution(* *..UserService..*(..))")
public void myPointCut() {
}
package com.company.aspect;
import com.company.proxy.User;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
@Override
public void login(String name, String password) {
System.out.println("UserServiceImpl.login");
//调用的是原始对象的login方法-- ->核心功能
/*
设计目的:代理对象的login方法--->额外功能+核心功能
AppliCationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
UserService userService = (UserService) ctx . getBean("userService");
userService.login();
不行:Spring工厂重量级资源一个应用中应该只创建一个工厂
*/
// this.register(new User());
UserService userService = (UserService) ctx.getBean("userService");
userService.register(new User());
}
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register");
}
}