spring-aop笔记
动态代理
实现方式:
jdk动态代理:使用jdk中的proxy,method,InvocationHandler创建对象。
jdk动态代理要求目标类必须实现接口
动态代理的作用
1、在目标类源代码不变的情况下,增加功能。
2、减少代码的重复。
3、专注业务逻辑代码。
4、解耦合,让业务功能和其他的功能分离。
使用方式
1、创建目标接口和实现类
package com.bjpowernode.service;
public interface SomeService {
void doSome();
void doOther();
}
package com.bjpowernode.service.impl;
import com.bjpowernode.service.SomeService;
import com.bjpowernode.utils.ServiceTools;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("执行业务方法 doSome ");
}
@Override
public void doOther() {
System.out.println("执行业务方法 doOther ");
}
}
2、创建要增加的功能
package com.bjpowernode.utils;
import java.util.Date;
public class ServiceTools {
public static void doLog(){
System.out.println(new Date());
}
public static void doTrans(){
System.out.println("提交事务");
}
}
3、创建InvocationHandler的实现类
package com.bjpowernode.handler;
import com.bjpowernode.utils.ServiceTools;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target = null;
//因为是动态的,所以对象的类型不固定,用Object修饰,传进来是什么对象,就给什么对象代理
public MyInvocationHandler(Object target) {
this.target = target;
}
//通过代理对象执行方法时,会调用这个方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy");
//增加的功能
ServiceTools.doLog();
//执行目标类方法
Object res = method.invoke(target,args);
//增加的功能
ServiceTools.doTrans();
return res;
}
}
4、创建代理对象,执行目标方法
package com.bjpowernode;
import com.bjpowernode.handler.MyInvocationHandler;
import com.bjpowernode.service.SomeService;
import com.bjpowernode.service.impl.SomeServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MyApp {
public static void main(String[] args){
//使用proxy创建代理对象
//1、创建目标类对象
SomeService target = new SomeServiceImpl();
//2、创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(target);
//3、创建proxy代理对象
SomeService proxy = (SomeService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
handler);
//通过代理对象,执行目标类方法
proxy.doSome();
}
}
AOP(面向切面编程)
基于动态代理,就是动态代理的一种规范化,把动态代理的实现步骤,方法都定义好,让开发人员用统一的方式,使用动态代理。
aop(Aspect Orient Programming)
Aspect :切面,给你的目标类增加的功能,就是切面。
Orient :面向
Programming :编程
1、需要在分析项目功能时,找出切面
2、合理安排切面的执行时间
3、合理安排切面的执行位置
术语
1)Aspect:切面,表示增强的功能,非业务功能。
2)JoinPoint:连接点,连接业务方法和切面的位置,就是某类中的业务方法。
3)Pointcut:切入点,指多个连接点方法的集合,多个方法。
4)目标对象:给那个类的方法增加功能,这个类就是目标对象。
5)Advice:通知:表示切面功能执行的时间。
------------------------------------------------------------------------------------
一个切面有三个关键的要素:
1、切面的功能代码,切面干什么
2、切面的执行位置,使用pointcut表示切面执行的位置
3、切面的执行时间,使用Advice表示,在目标方法前还是后
Aspectj实现AOP
aspectj实现方式:
1、xml文件方式:配置全局事务
2、注解方式:有5个注解
注解方式
切面的执行时间
在aspectj中使用注解表示,也可以使用xml配置文件中的标签
1)@Before
2)@AfterReturning
3)@Around
4)@AfterThrowing
5)@After
切入点表达式
execution ([modifiers-pattern]? returnType-pattern [declaring-type-pattern]?name- pattern(param-pattern) [throws-pattern]? )
↕↕↕↕
execution(访问权限 方法返回值类型 全路径类名+方法声明(参数) 异常类型)
一共5个参数,有问号(?)的表示可以省略,每一个参数之间通过空格分隔,
返回值类型和方法声明(参数)一定不可以省略
切入点表达式通配符
通配符 含义
-
0到多个任意字符
… 用在方法参数中,表示任意多个参数。 用在包名后,表示当前包以及子包路径
-
用在类名后,表示当前类及其子类。用在接口后,表示当前接口及其实现类
通配符示例
1、 execution(public * *(..)) -> 任意的public方法
2、 execution(* set*(..)) ->任意以set开头的方法
3、 execution(* com.xyz.service.*.*(..)) ->在service包下的任意类的任意方法
4、 execution(* com.xyz.service..*.*(..)) ->在service包和子包下的任意类的任意方法
5、 execution(* *..service.*.*(..)) ->所有的包下的service包的所有类的所有方法
使用方法
使用aspectj实现aop,目的是给已经存在的类和方法,增加一些功能,前提是不改变原来的代码
1、新建Maven项目
2、加入依赖
1)spring依赖
2)aspectj依赖
3、创建目标接口和实现类
4、创建一个切面类,就是一个普通类
1)在类上加上@Aspect注解
2)在类中定义方法,就是切面要执行的功能代码
在@Aspect注解上加入通知注解,例如@Before,需要指定切入表达式
5、创建spring配置文件,声明对象,把对象交给spring容器统一管理,
可以通过xml方式或注解方式
1)声明目标类对象
2)声明切面类对象
3)声明Aspectj框架中的自动代理生成器。
自动代理生成器:用来完成代理对象的自动创建功能的。
6、测试:从spring容器中胡获取目标对象(实际上就是代理对象)。
通过代理对象执行目标类的方法,实现aop。
XML方式一
切面类实现MethodBeforeAdvice接口,或者MethodAfterAdvice等接口,
然后重写接口方法,通过这个接口和重写的方法,就可以知道在目标方法执行前还是后执行切面方法。
//切面类
public class MyAspect implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("输出时间:"+new Date());
}
}
deptServiceImpl:目标类对象
aspect:切面类对象
cut:切入点表达式,确定目标方法
<bean id="deptServiceImpl" class="com.bjpowernode.service.DeptServiceImpl"/>
<bean id="aspect" class="com.bjpowernode.aspect.MyAspect"/>
<aop:config>
<aop:pointcut id="cut" expression="execution(* com.bjpowernode.service.DeptServiceImpl.*(..))"/>
<aop:advisor advice-ref="aspect" pointcut-ref="cut"/>
</aop:config>
XML方式二
//切面类,不实现任何接口,方法也是普通方法
public class MyAspect {
public void beforeAspect(){
System.out.println("前置通知2");
}
}
deptserviceImpl:目标类对象
aspect:切面类对象,在没有在aop:aspect标签使用时,就是一个普通对象
因为切面类没有实现方式一的接口,通过<aop:aspect ref=“aspect”>来确定切面对象
cut:切入点表达式,确定目标方法
aop:before标签 :确定执行切面方法的时机
method=“beforeAspect”:确定那个方法时切面方法
通过aop:aspect ref=“aspect”,确定切面对象,然后aop:before method=“beforeAspect” ,确定切面方法和执行时机,pointcut-ref="cut"确定目标类对象和目标方法
通过getBean方法获得目标对象的代理对象
<!--目标对象-->
<bean id="deptserviceImpl" class="com.bjpowernode.impl.DeptServiceImpl"/>
<!--切面对象-->
<bean id="aspect" class="com.bjpowernode.aspect.MyAspect"/>
<!--AOP-->
<aop:config>
<aop:aspect ref="aspect">
<aop:pointcut id="cut" expression="execution(* com.bjpowernode.impl.DeptServiceImpl.*(..))"/>
<aop:before method="beforeAspect" pointcut-ref="cut"/>
</aop:aspect>
</aop:config>
AOP实现事务
事务也是AOP的一种,只是spring框架把事务做好了,相当于框架就已经把事务的切面类写好了,我们只需要写目标类就可以了。
事务相当于环绕通知。
XML方式
事务需要的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.18</version>
</dependency>
transcation:事务管理对象,需要给他一个数据源
service:目标对象
aspect:spring框架实现好的事务切面类
tx:method name="*",给所有的方法都加上事务
<bean>
<!--数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="Yjznzsa666"/>
<property name="maxActive" value="20"/>
</bean>
<!--目标对象-->
<bean id="service" class="com.bjpowernode.impl.DeptServiceImpl"/>
<!--事务管理对象-->
<bean id="transcation" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--切面对象,由spring框架提供-->
<tx:advice id="aspect" transaction-manager="transcation">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="cut" expression="execution(* com.bjpowernode.impl.DeptServiceImpl.*(..))"/>
<aop:advisor advice-ref="aspect" pointcut-ref="cut"/>
</aop:config>
</beans>