AOP
简介:
概念
AOP:Aspect Oriented Programming 面向对象编程,是OOP面向对象的一种补充。
将程序中交叉业务逻辑(事物、日志)代码提取出来,封装成切面,由AOP在合适的位置将它封装的切面动态的织入到具体业务逻辑中。
AOP不是spring特有的
原理;
spring原理就是使用动态代理
- 对于实现接口的目标类,使用的是jdk动态代理
- 对于没有实现任何接口的目标列,使用的是cglib动态代理
应用的场合
适用于具有很切逻辑的场合,比如事物管理、日志记录、性能检测、异常通知、访问控制等。
作用:
- 在不改变原有代码基础上动态添加新功能。
- 模块化
术语
- 连接点 JoinPoint
程序执行的某个特定位置,如方法调用前、方法调用后、方法抛出异常时、方法调用前后等。 - 切入点 Pointcut
定位查找到需要连接的点,即切点 - 增强Advice 也成为通知
在切点上执行一段代码程序,用来实现某些功能 - 目标对象 Traget
将执行增强处理的目标类 - 织入Weaving
将增强添加到目标类具体切入点上的过程 - 代理 Proxy
一个类被织入增强后,会产生一个代理类 - 切面 Aspect
切面和增强的组合 - 引介 Introduction 也成为引入
实现原理:
代理模式回顾
- 概念:
为其他对象提供一种代理,以控制对这个对象的访问,起到中介的作用。通过代理对象访问目标对象,可以增强额外的操作,扩展目标对象的功能。
代理三要素
- 目标类的接口
- 目标类的实例
- 交叉业务逻辑,要执行的操作
代理分类
静态代理
- 解释:
代理类是程序员创建或者工具生成。所谓静态代理就是程序运行之前你在代理类的字节码文件 - 缺点:
代理对象需要和目标对象实现相同的接口,如果接口增加方法,目标对象和代理对象都要维护。 - 静态代理实操:
service 接口:
public interface UserService {
void login(String username, String password) throws NoSuchMethodException;
public String logout() throws NoSuchMethodException;
}
service 接口实现类:
public class UserServiceImpl implements UserService {
@Override
public void login(String username, String password) {
System.out.println("username:"+username+"password:"+password);
}
@Override
public String logout() {
System.out.println("UserServiceImpl.logout");
return "bytebyte";
}
}
代理类:
public class UserServiceProxy implements UserService { //目标类的接口
private UserService userService=new UserServiceImpl(); //代理目标实例
@Override
public void login(String username, String password) throws NoSuchMethodException {
/* System.out.println("login start at:["+new Date().getTime()+"]");
userService.login(username, password);*/
invoke(userService.getClass().getMethod("login",String.class,String.class),username,password); //具体业务逻辑
}
@Override
public String logout() throws NoSuchMethodException {
/* System.out.println("logout start at:["+new Date().getTime()+"]");
return userService.logout();*/
return invoke(userService.getClass().getMethod("logout")).toString();
}
//封装到公共方法中
private Object invoke(Method method, Object... args)
{
//1.打印日记
System.out.println(method.getName()+"login start at:["+new Date().getTime()+"]");
try {
return method.invoke(userService,args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
main入口
public static void main(String[] args) {
UserService userservice=new UserServiceProxy();
try {
System.out.println(userservice.logout());
userservice.login("zhangsan","里斯");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
动态代理
代理类是程序在运行期间由JVM根据反射等机制动态生成的,自动生成代理类和代理对象。所谓动态代理就是程序在运行前不存在代理类的字节码文件。
动态代理的三种技术:
Proxy
特点
JDK自带技术,使用Jdk自带的Proxy必须实现一个或多个接口,如果没有实现任何接口,则无法使用jdk自带的Proxy技术。
用法
- 语法:
Proxy.newProxyInstance{
classLoader,//目标类的加载器
interfaces,// 目标累的接口
InvocationHandler 交叉业务逻辑
}
- 实现步骤:
- UserService
public interface UserService {
void login(String username, String password) throws NoSuchMethodException;
public String logout() throws NoSuchMethodException;
}
- UserServiceImpl
public class UserServiceImpl implements UserService {
@Override
public void login(String username, String password) {
System.out.println("username:"+username+"password:"+password);
}
@Override
public String logout() {
System.out.println("UserServiceImpl.logout");
return "bytebyte";
}
}
- main:
public static void main(String[] args) throws NoSuchMethodException {
UserService userservice = (aop02.service.UserService)Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(),//目标类的加载器
new Class[]{UserService.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法名:" + method.getName());
try{
System.out.println("开始事务");
Object obj=method.invoke(UserServiceImpl.class, args);
System.out.println("提交事务");
return obj;
}
catch (Exception ex)
{
System.out.println("回滚事务");
}
return null;
}
}
);
System.out.println(userservice.logout());
userservice.login("张三", "123456");
}
cglib
解释
如果没有实现接口,可以使用cglib,它是通过继承来实现。
语法
Enhancer.create(
Class type,
Callback callback
)
实现步骤
- 添加jar包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
- 使用cglib
HelloWorld helloWorld = (HelloWorld) Enhancer.create(
HelloWorld.class, //目标类的类型
new InvocationHandler() { //交叉业务逻辑
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + " start at:[" + new Date().getTime() + "]");
return method.invoke(new HelloWorld(), args);
}
}
);
helloWorld.sayHello();
Spring AOP配置方式
Spring 增强的类型
通知类型 实现接口 描述
前置通知 MethodBeforeAdvice 在方法执行前添加功能
后置通知 AfterReturnAdvice 在方法执行后添加功能
环绕通知 MethedInterceptor 在方法前后执行
异常通知 ThrowsAdvice 异常通知
引入通知 IntroductionInterceptor 在目标类添加新方法和属性
注:多个通知之间不允许有耦合,即多个Advice之间不允许有业务交叉。像事务处理就必须使用环绕通知
Spring AOP1.x
- 使用ProxyFactoryBean 手动代理
使用步骤:
- 添加jar 包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
- 定义接口
public interface UserService {
public void login(String username, String password) throws ClassNotFoundException;
public String logout();
}
- 实现接口
public class UserServiceImpl implements UserService {
@Override
public void login(String username, String password) {
System.out.println("username:"+username+"password:"+password);
System.out.println("UserServiceImpl.login");
}
@Override
public String logout() {
System.out.println("UserServiceImpl.logout");
return "bytebyte";
}
}
- 配置Advice 实现相应的接口
public class logAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(method.getName()+" start at:["+new Date().getTime()+"],args:"+ Arrays.toString(args)+",target:"+target);
}
}
- xml文件配置:
<bean class="aop05.service.impl.UserServiceImpl " id="UserServicetarget"/>
<bean class="aop05.Advice.logAdvice " id="logAdvice"/>
<bean class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor" id="logAdvisor">
<property name="advice" ref="logAdvice"/>
<property name="mappedNames">
<list>
<value>login</value>
<value>logout</value>
</list>
</property>
</bean>
<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="calcService">
<property name="target" ref="UserServicetarget"/>
<property name="interfaces">
<list>
<value>aop05.service.UserService </value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>logAdvisor</value>
</list>
</property>
</bean>
- 使用
public class test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("ioc04/spring.xml");
UserService userService = (UserService) ac.getBean("userService");
try {
userService.login("sss", "ffff");
System.out.println(userService.logout());
System.out.println(userService.getClass());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Spring AOP 2.x 配置
简介
基于命名空间的配置,原来是使用后处理器,更简单
特点:
- 简化配置
- 非入侵性:编写通知时不需要实现任何接口
- 使用AspectJ表达式定义切点
介绍:
一种表达式,用来定义切点位置
两种用法- within(包名.类型)匹配类中的所有方法
- execution(返回值类型 包名.类名.方法名(参数类型)) 可使用通配符*和..
execution(void aop06.service.impl.UserServiceImpl.login(String,String))
四种写法
方法名 说明
public void 方法名(JoinPoint) 一般用于前置通知
public void 方法名(JoinPoint,Object) 有返回值,一般用户后置通知
public void 方法名(JoinPoint,Exception)
异常通知
public void 方法名(ProceedingJoinPoint)
环绕通知
步骤:
- 定义接口
public interface UserService {
public void login(String username, String password) throws ClassNotFoundException;
public String logout();
}
- 实现接口
public class UserServiceImpl implements UserService {
@Override
public void login(String username, String password) {
System.out.println("username:"+username+"password:"+password);
System.out.println("UserServiceImpl.login");
}
@Override
public String logout() {
System.out.println("UserServiceImpl.logout");
return "bytebyte";
}
}
- 定义增强类,不需要实现任何接口。
public class logAdvice {
public void log(JoinPoint joinPoint)
{
System.out.println("joinPoint.getArgs():"+ Arrays.toString(joinPoint.getArgs()));
System.out.println("joinPoint.getClass():"+joinPoint.getClass());
System.out.println("joinPoint.getThis():"+joinPoint.getThis());
System.out.println("joinPoint.getTarget():"+joinPoint.getTarget());
Signature signature = joinPoint.getSignature();
MethodSignature signaturemethod=(MethodSignature)signature;
Method method = signaturemethod.getMethod();
System.out.println("method.getName():"+method.getName());
}
}
- 配置
<!---Spring 2.x配置 -->
<!---配置目標對象 -->
<bean id="userService" class="aop06.service.impl.UserServiceImpl"/>
<!---配置增強類 -->
<bean id="logAdvice" class="aop06.advice.logAdvice"/>
<!---配置pointCut 并织入 -->
<aop:config>
<!---配置pointCut -->
<!-- <aop:pointcut id="pc" expression="within(aop06.service.impl.UserServiceImpl)"/>-->
<!-- <aop:pointcut id="pc" expression="within(aop06.service.impl.*ServiceImpl)"/>-->
<aop:pointcut id="pc" expression="execution(void aop06.service.impl.UserServiceImpl.login(String,String))"/>
<aop:aspect ref="logAdvice">
<!---將logAdvice中的log方法以前入通知的方式织入到pc中-->
<!-- <aop:before method="log" pointcut-ref="pc"/>-->
<!-- <aop:after-returning method="log2" pointcut-ref="pc" returning="returnValue"/>-->
<!--<aop:after-throwing method="log3" pointcut-ref="pc" throwing="ex"/>-->
<aop:around method="log4" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>
- 测试
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("aop06/spring.xml");
UserService userServicetarget = (UserService)ac.getBean("userService");
try {
userServicetarget.login("zhanghua","lisi");
userServicetarget.logout();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}