Spring-AOP学习
通过java的代理模式将系统性的服务和业务代码剥离,对特定的代码进行增强处理
java代理
代理模式是一种设计模式,通过代理访问目标对象并执行相应的方法,可以在目标对象实现的基础上,增加额外的功能动作,实现功能的增强
静态代理
例如有一个IUserDao
的接口,并且拥有Save()
方法,并且有一个UserDao
的类实现了这个接口
public interface IUserDao {
void save();
}
public class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("-----已经保存数据!!!------");
}
}
而我们需要在save
中添加上事务的操作时,就可以增加一个代理,在代理中开关事务,而实际的对数据的操作委托给UserDao
public class UserDaoProxy implements IUserDao{
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
@Override
public void save() {
System.out.println("开始事务...");
target.save(); // 执行目标对象的方法
System.out.println("提交事务...");
}
}
// 使用
public class Main {
public static void main(String args[]){
IUserDao target = new UserDao();
IUserDao executor = new UserDaoProxy(target);
target.save();
}
}
动态代理
然而,大量使用静态代理还是需要很多冗余的代码,因此java中的proxy包支持基于反射实现动态代理机制,优点如下:
- 不用实现目标接口的方法
- 动态地在内存中构建代理对象
基于proxy的动态代理
public class ProxyFactory{
//维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//给目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务2");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
}
);
}
}
public class App {
public static void main(String[] args) {
IUserDao target = new UserDao();
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
proxy.save();
}
}
使用proxy代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
基于cglib的动态代理
通过构建目标对象子类的方式实现代理,并不需要增强的方法需要实现某个特定的接口
- 它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
- 底层是通过使用字节码处理框架ASM来转换字节码并生成新的子类
- 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
public class ProxyFactory implements MethodInterceptor{
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
public class App {
@Test
public void test(){
//目标对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.save();
}
}
使用cglib的动态代理方式不再需要代理的方法必须实现某个接口,实际的作用相当于再内存中创建了一个增强的子类,并拦截目标对象的方法,增强后被调用
Spring-AOP
概念介绍
AOP 实际上是由目标类的代理类实现的。AOP 代理其实是由 AOP 框架动态生成的一个对象,该对象可作为目标对象使用,在java中有两种实现的方式:
- Spring 默认使用 Java 动态代理来创建 AOP 代理, 这样就可以为任何接口实例创建代理了。
- 当需要代理的类不是代理接口的时候, Spring 自动会切换为使用 CGLIB 代理,也可强制使用 CGLIB。
在使用的时候,我们只需要关注以下三点内容:
- 定义普通业务组件。
- 定义切入点,一个切入点可能横切多个业务组件。
- 定义增强处理,增强处理就是在 AOP 框架为普通业务组件织入的处理动作。
使用示例
注解模式
在配置文件中启用注解模式开关,在配置文件中新增以下内容:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
定义一个切面,里面包含对于切入点的一系列操作,包括Before,After,AfterReturing,AfterThroughing,Around
- 对于切面首先使用@Aspect注解定义切面
- 可以使用@Pointcut定义切入点,之后的注解可以使用切入点的函数来代替
- 针对切入点的不同的操作,使用注解定义不同的增强函数,也可以不需要定义切入点,直接定义各阶段拦截器
@Component
@Aspect
public class AOP {
@Pointcut("execution(* org.example.Dao.*(..))")
public void aop(){
}
//里面的值为切入点表达式
// @Before("execution(* org.example.Dao.*(..))")
// public void begin() {
// System.out.println("开始事务");
// }
//
//
// @After("execution(* org.example.Dao.*(..))")
// public void close() {
// System.out.println("关闭事务");
// }
@Before("aop()")
public void begin() {
System.out.println("start transaction");
}
@After("aop()")
public void close(){
System.out.println("end transaction");
}
}
语法解析
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
符号解析
- ? 表示0或1,可以不写
- '*'表示任意的类型,0 或多
- 方法的参数为…表示为可变参数
参数解析
- modifiers-pattern?【修饰的类型,可以不写】
- ret-type-pattern【方法返回值类型,必写】
- declaring-type-pattern?【方法声明的类型,可以不写】
- name-pattern(param-pattern)【要匹配的名称,括号里面是方法的参数】
- throws-pattern?【方法抛出的异常类型,可以不写】
示例
execution(public * *(..))
执行public修饰的,任意返回值,任意方法名的函数
execution(* set*(..))
执行任意返回值,以set命名开头的方法
execution(* com.xyz.service.AccountService.*(..))
执行任意返回值的,com.xyz.service.AccountService类下的任意方法名,任意返回值的函数
execution(* com.xyz.service.*.*(..))
执行com.xyz.service包下任意方法名,任意返回值的函数
execution(* com.xyz.service..*.*(..))
执行com.xyz.service的任意子包所有方法