AOP(面向切面编程)
springAOP底层实现是使用代理模式!
静态代理模式的角色:
- 抽象角色:一般使用接口或实现类类解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理角色会增加一些附属操作
- 客户:访问代理对象的人
代理模式的好处:
- 使真实角色更加纯粹,专注自身的业务,而不同考虑一些公共的业务
- 公共业务交给代理对象!实现了业务的分工!
- 对于扩展业务使用扩展代理类中的业务即可!方便几种管理!
静态代理的缺点:
- 一个真实对象就会产生一个代理对象;增加了代码量!
动态代理可以解决代码量增加的问题,因为动态代理使用了反射机制! 静态代理在编译时就代理了,动态代理在运行时才进行代理。
- 动态代理的代理类是动态生成的,不是我们直接写的。
Spring中AOP大量使用了代理模式进行横向开发!
实现AOP需要拥有的元素
AOP的作用:提供声明式的事务,允许用户自定义切面!
- 横切关注点:要横切进入的功能,例如:日志、安全等等。
- 切面:横切关注点模块化成的类。(因为横切关注点可能有很多逻辑,放到一个类中好管理。其中放需要扩展的业务方法)
- 通知:横切关注点模块化成的类的方法。(就是一个方法就是要扩展业务的方法)
- 目标:真实对象。
- 代理:代理对象。
- 切入点:“切面通知”执行的地方的定义。(就是插入扩展业务的地方)
- 链接点:切入点执行的地方。(就是运行代理对象方法的那个地方)
SpringAOP中,通过Advice定义横切逻辑,Spring支持5中Advice:(也就是说用了spring提供的Advice就可以不改变原有代码增加新功能,在连接点增加功能)
通知类型 | 连接点 | 实现接口 |
---|---|---|
前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.springframework.aop.MethodInterceptor |
异常抛出通知 | 方法异常时 | org.springframework.aop.ThorowAdvice |
引介通知 | 类中增加新的方法属性时 | org.springframework.aop.IntroductionInterceptor |
使用spring实现AOP
使用spring的API接口实现切面类
使用spring的API接口实现切面类,从而达到AOP!
使用AOP之前需要导入AOP的包!
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
public interface UserService {
void add();
void delete();
void modify();
Object select();
}
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加\n");
}
public void delete() {
System.out.println("删除\n");
}
public void modify() {
System.out.println("修改\n");
}
public Object select() {
System.out.println("查询\n");
return null;
}
}
// 实现spring提供的接口创建切面
public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("在"+target.getClass().getName()+"中的方法"+method.getName()+"之前执行了!");
}
}
<?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 id="userService" class="pers.qiu.service.UserServiceImpl"></bean>
<bean id="log" class="pers.qiu.log.Log"></bean>
<aop:config>
<!--需要一个切入点 id 表达式=execution(返回值 类名.方法名(参数) ) 表示执行的位置-->
<!--.*代表所有方法,..代表接收一切参数-->
<aop:pointcut id="pointcut" expression="execution(* pers.qiu.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加,就是将log这个切面增加到切入点方法中.它就自动的环绕在业务逻辑之前或之后.-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
<!-- 也可以使用多个切入点
<aop:config>
<aop:pointcut id="addpointcut" expression="execution(* pers.qiu.service.UserServiceImpl.add(..))"/>
<aop:pointcut id="deletepointcut" expression="execution(* pers.qiu.service.UserServiceImpl.add(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="addpointcut"/>
<aop:advisor advice-ref="log" pointcut-ref="deletepointcut"/>
</aop:config>
-->
</beans>
使用了aop配置后,在getBean时获取到的是user Service的代理对象.获取在生成代理对象到spring容器中的时候就已经插入完成了.
public class MyTest {
@Test
public void text01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
有了AOP之后,可以看到:真实类中只写它的业务逻辑;切面类中只写扩展的逻辑;通过xml进行连接配置即可生成连接两者功能的代理类!
使用自定义类实现切面类
使用自定义的类作为切面类,而不使用spring的包中提供的那四种类作为切面类!
// 自定义切面
public class MyCutFact {
public void before(){
System.out.println("在切点前面执行!");
}
public void after(){
System.out.println("在切点后面执行!");
}
}
<?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 id="userService" class="pers.qiu.service.UserServiceImpl"></bean>
<bean id="log" class="pers.qiu.log.Log"></bean>
<bean id="cutFact" class="pers.qiu.log.MyCutFact"></bean>
<aop:config>
<!--切面-->
<aop:aspect ref="cutFact">
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* pers.qiu.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
可以看到:使用AOP需要:
- 切面类
- 切入点
- 切面类使用的通知
自定义切面与使用spring接口实现切面类的区别:
- 使用spring接口实现切面类,它已经将切面类功能特向化了。也就是实现指定的接口只能在切入点前面或者后面或者环绕进行。而自定义切面类并没有指定的重写方法,所以只能将方法命名为before、after进行通知的设定。
- 所以在xml中需要将切面显示声明出来(实现spring接口的方式spring已经封装了所以不需要做)
- 使用的通知也需要显示声明出来(实现spring接口的方式spring封装了,并且spring特向化了所以不需要做)
- 都需要声明一个切入点!
使用注解实现AOP
@Aspect
@Component
public class AnnotationCutFect {
// 直接在通知上定义切入点,创建AnnotationCutFect对象时直接将切入点内置到里面。getBean时直接得到组合后的代理对象。
@Before("execution(* pers.qiu.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前!");
}
}
@Configuration
@ComponentScan({"pers.qiu.log", "pers.qiu.service"})
@Component
// 开启自动代理(必开,否则不会代理它)
@EnableAspectJAutoProxy
public class MyApplicationContext {
@Bean
public UserServiceImpl userService(){
return new UserServiceImpl();
}
@Bean
public AnnotationCutFect annotationCutFect(){
return new AnnotationCutFect();
}
}
public class MyTest {
@Test
public void test02(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyApplicationContext.class);
UserService userService = (UserService) context.getBean("userService");
AnnotationCutFect annotationCutFect = (AnnotationCutFect) context.getBean("annotationCutFect");
userService.add();
}
}
如果不在MyApplicationContext中开启自动代理,则需要使用xml进行开启配置并且使用XMLApplicationContext获取spring容器。(太麻烦,要不就全注解,要不就全配置文件,混搭八太行!):
<?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">
<aop:aspectj-autoproxy/>
</beans>