目录
一、AOP理解
- 面向切面编程,是OOP面向对象编程的补充。将程序中交叉的业务逻辑代码提取出来,封装成切面,由AOP容器在适当的时机将封装的切面动态的织入到具体业务逻辑中。
AOP原理-->使用代理
- 对于实现接口的目标类,使用jdk动态代理
- 对于没有实现任何接口的类,使用cglib代理
术语
- 连接点 joinpoint
程序执行中某一特定位置,eg 方法调用前或后,方法抛出异常
- 切入点 pointcut
定位查找到需要的连接点,一个切点可以包含多个切入点
- 增强 Advice 也成为通知
在切点上执行的一段代码,用来实现某些功能
- 目标对象 target
将执行增强处理的目标类
- 织入 weaving
将增强添加到目标类中具体切入点的过程
- 代理
一个类被织入增强后,会产生一个代理类,这个类包含原类以及增强
- 切面
切点和增强的组合
- 引介/引入 introduction
二、代理模式
概念
- 为其他对象提供一种代理,以控制对这个对象的访问,起到中介的作用
通过代理对象访问目标对象,可以增强额外的操作,扩展目标对象的功能
静态代理
- 手动创建
代理对象需要与目标类实现相同接口,维护麻烦
动态代理
- 由JVM根据反射动态生成,程序运行前不存在代理类的字节码文件
JDK
目标对象必须实现一个或多个接口
- eg
@Test
public void test() {
UserSrv userSrv = (UserSrv) Proxy.newProxyInstance(
UserSrvImpl.class.getClassLoader(),//目标类的类加载器
new Class[]{UserSrv.class},//目标类的接口列表
new InvocationHandler() {//交叉业务逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 打印日志
System.out.println(method.getName() + " 打印日志 ---" );
//执行业务逻辑
Object o = method.invoke(new UserSrvImpl(),args);
System.out.println(o);
System.out.println();
return o;
}
});
userSrv.login("XIXI","0000");
System.out.println(" ---------- ---------- --------");
userSrv.logout();
System.out.println(userSrv.getClass());
}
public class UserSrvImpl implements UserSrv{
@Override
public String login(String usrNm,String pwd) {
return usrNm +"login success at "+ new Date().getTime();
}
@Override
public String logout() {
return "now 一位用户离开------ ";
}
}
CGLIB
- eg
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
@Test
public void testCglib(){
App app = (App) Enhancer.create(
App.class, // 目标类的类型
new net.sf.cglib.proxy.InvocationHandler() { //交叉业务逻辑
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println(method.getName() + " start ---- ");
return method.invoke(new App(),objects);
}
}
);
app.HelloWord(new String[]{"XIXI"});
//代理类的类型
System.out.println(app.getClass());
}
public class App {
public static void main(String[] args) {
System.out.println("Hello World!");
}
public String HelloWord(String[] args) {
System.out.println();
System.out.println("args: "+ Arrays.toString(args));
System.out.println("null list: " + Arrays.asList(new ArrayList<String>()));
System.out.println();
return "Hello World!";
}
}
三、AOP配置方式
Spring AOP 1.X (了解,有助于理解AOP)
- 使用ProxyFactoryBean手动代理
五种增强(/通知)类型
通知类型 | 实现接口 | 描述 | 备注 |
---|---|---|---|
前置通知 | MethodBeforeAdvice | 在方法执行前添加功能 | |
后置通知 | AfterReturningAdvice | 在方法执行后添加功能 | |
环绕通知 | MethodInterceptor | 在方法执行前后添加功能 | org.aopalliance.intercept.MethodInterceptor |
异常通知 | ThrowsAdvice | 在方法抛出异常后添加功能 | |
引入通知 | IntroductionInterceptor | 在目标类中添加新方法和属性 | 了解 |
实例
- 1.配置pom
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
- 2.配置Advice
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="logAdvice" class="org.example.LogAdvice"></bean>
</beans>
- 3.定义增强类,实现相应接口
public class LogAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("method "+method.getName()+" args: "+ Arrays.toString(objects) + " target: " +o );
}
}
- 4.配置pointcut
<!-- ADVISOR = POINTCUT + ADVICE -->
<!-- 将ADVICE注入到POINTCUT位置,织入的过程 -->
<bean id="logAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<!-- 指定advice-->
<property name="advice" ref="logAdvice"></property>
<!-- 指定匹配的方法-->
<property name="mappedNames">
<list>
<value>login</value>
</list>
</property>
</bean>
- 配置代理
<!-- 配置代理 -->
<bean id="userSrv" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 目标类实例 -->
<property name="target" ref="userSrvTarget"></property>
<!-- 目标类接口列表 -->
<property name="interfaces">
<list>
<value>org.example.UserSrv</value>
</list>
</property>
<!-- 交叉业务逻辑 -->
<property name="interceptorNames">
<list>
<value>logAdvisor</value>
</list>
</property>
</bean>
Spring AOP 2.X (了解)
- 基于命名空间的代理
- 简化配置
- 非侵入性,编写通知时不需要写任何的接口
- 使用AspectJ表达式定义切点
四种增强类型
写法 | 说明 | 备注 |
---|---|---|
public void XXX(JoinPoint joinPoint) | 前置通知 | |
public void XXX(JoinPoint joinPoint,Object o) | 后置通知 | |
public void XXX(JoinPoint joinPoint,Exception e) | 异常通知 | |
public Object XXX(ProceedingJoinPoint p) | 环绕通知 |
实例
- 1.需要增强的目标类实例
- 2.配置增强类
- 3.配置pointcut,并织入
(匹配类中的所有方法)
<?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="userSrv" class="org.example.aop2.UserSrvImpl"></bean>
<!-- 配置advice -->
<bean id="logAdvice" class="org.example.aop2.LogAdvice"></bean>
<!-- 配置pointcut并织入 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pc" expression="within(org.example.aop2.UserSrvImpl)"/>
<!-- 织入 -->
<aop:aspect ref="logAdvice">
<!-- 将LogAdvice中的log方法以前置通知的方式织入到UserSrvImpl-->
<aop:before method="preAdvice" pointcut-ref="pc"></aop:before>
<aop:after-returning method="postAdvice" pointcut-ref="pc" returning="val"></aop:after-returning>
<aop:after-throwing method="exceptionAdvice" pointcut-ref="pc" throwing="e"></aop:after-throwing>
<aop:around method="processAdvice" pointcut-ref="pc" ></aop:around>
</aop:aspect>
</aop:config>
</beans>
public class LogAdvice {
/**
* 前置增强
* @param joinPoint
*/
public void preAdvice(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature(); //签名
String methodName = signature.getName(); //方法名
MethodSignature methodSignature = (MethodSignature)signature; // 转换为方法签名
Method method = methodSignature.getMethod();
Object[] args = joinPoint.getArgs(); //方法参数
Object target = joinPoint.getThis(); // 目标对象
Object target1 = joinPoint.getTarget();
System.out.println();
System.out.println("signature: "+signature);
System.out.println("methodName: "+methodName);
System.out.println("method: "+method);
System.out.println("args: "+ Arrays.toString(args));
System.out.println("target: "+target);
System.out.println("target1: "+target1);
System.out.println();
System.out.println();
}
/**
* 后置通知
* @param joinPoint
* @param val
*/
public void postAdvice(JoinPoint joinPoint,Object val){
System.out.println("postAdvice----------- val:"+val);
}
/**
* 异常通知
* @param joinPoint
* @param e
*/
public void exceptionAdvice(JoinPoint joinPoint,Exception e){
System.out.println("exceptionAdvice ----- e: "+e);
}
/**
* 环绕通知
* @param proceedingJoinPoint
*/
public Object processAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("processAdvice pre __");
Object o = proceedingJoinPoint.proceed();
System.out.println("processAdvice post __");
return o;
}
}
within
- 1.通配符*
<aop:pointcut id="pc" expression="within(org.example.aop2.*Impl)"/>
- 2.作用在类中所有方法
execution (常用)
- 1.通配符*和..
- 2.更加精确execution(返回值类型包名.类名.方法名(参数类型))
<!-- 第1个*:返回值类型任意 -->
<!-- 第2个*:匹配aop1包下所有的类 -->
<!-- 第3个*:匹配类下的所有方法 -->
<!-- (..): 参数任意 -->
<aop:pointcut id="pc" expression="execution(* org.example.aop1.*.*(..))"/>
Annotation,基于注解的配置(推荐)
扫描包,数据装配
IOC注解
扫描包
package org.example.ioc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
//@Component: 默认bean的id为类名首字母小写-springBean,可通过(value = "beanId")修改名称
@Component(value = "SpringBean")
public class SpringBean {
@Value("${name}")
private String name;
@Value("${gender}")
private String gender;
@Value("${tag}")
private String tag;
/**
* @Autowired:Spring提供的自动注解 默认按照byType, 如有多个同类型则按照byName
* 方式1:结合@Qualifier("springBean")则按照byName
* 方式2:@resource,JavaEE提供
*/
@Autowired
private OtherBean otherBean;
@Autowired
@Qualifier("list")
private List list;
@Resource
private Integer[] array;
@Resource
private Map<String, String> map;
@Resource
private Set<String> set;
@Resource
private Properties properties;
<!-- 扫描包 -->
<context:component-scan base-package="org.example.ioc"></context:component-scan>
<context:component-scan base-package="org.example.srv.impl"></context:component-scan>
<context:component-scan base-package="org.example.dao.impl"></context:component-scan>
数据装配
<context:property-placeholder location="classpath:user.properties"></context:property-placeholder>
<!-- 数据装配 -->
<util:list id="list">
<value>"1"</value>
<value>"2"</value>
</util:list>
<util:list id="array">
<value>1</value>
<value>1</value>
<value>2</value>
</util:list>
<util:map id="map">
<entry key="1" value="1"></entry>
<entry key="2" value="2"></entry>
</util:map>
<util:set id="set">
<value>2</value>
<value>2</value>
<value>3</value>
</util:set>
<util:properties id="properties">
<prop key="key1">1</prop>
</util:properties>
生命周期
@PostConstruct
public void init(){
System.out.println("init()");
System.out.println();
}
@PreDestroy
public void destory(){
System.out.println("init()");
}
bean实力话时机
- 类注解
@Lazy(value = false)//单例情况下默认为value = false,可value = true设置,非懒加载
作用域
- 类注解
@Scope(value = "singleton") //value = "prototype(非单例)—/singleton(单例)"
AOP注解
- xml
<context:component-scan base-package="org.example.srv.impl"></context:component-scan>
<context:component-scan base-package="org.example.dao.impl"></context:component-scan>
<context:component-scan base-package="org.example.aop"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAdvice {
@Pointcut("execution(String org.example.*.impl.*.*(..))")
private void pointCut(){
System.out.println("pointCut()");
}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
System.out.println();
System.out.println("Aspect-before --");
System.out.println();
}
@AfterReturning(pointcut = "pointCut()",returning="returnVal")
public void after(JoinPoint joinPoint, String returnVal){
System.out.println();
System.out.println("Aspect-after");
}
@AfterThrowing(pointcut = "pointCut()",throwing = "e")
public void throwing(JoinPoint joinPoint, Exception e) {
System.out.println();
System.out.println("Aspect-throwing");
System.out.println(joinPoint.getSignature().getName() + " Exception e: " + e);
System.out.println();
}
@Around(("pointCut()"))
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println();
System.out.println("Aspect-around before---");
Object val = null;
try {
val = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
System.out.println("catch");
throwable.printStackTrace();
}
System.out.println("Aspect-around after---");
System.out.println();
return val;
}
}