oop(面向对象编程):面向对象,就是纵向地将事物给封装成类,里面具有这种事物的属性和行为。当别人想用到这种事物时,就通过构造它的一个实例对象来获得。体现出一种封装性。
aop(面向切面编程):横向地对不同事物的抽象,属性与属性,方法与方法,对象与对象都可以组成一个切面。简单来说,aop思想就是可以将某些类里的属性方法等抽取出来进行处理,组成一个新方法。
下面来张图说明一下:
可以看到,这里有2个对象A和B,它们各自有对应的方法。现在,我想用B对象里的方法对A对象方法进行处理(增强扩展A对象方法)。那么我就会重新创建一个对象出来作为A对象的代理(Proxy)对象,这个对象,他就来实现A对象方法的处理。这就是Aop体现的思想。
来段代码实现上图
首先,创建目标对象service
public class UserServiceImpl implements UserService {
public void show1() {
System.out.println("show1....");
}
public void show2() {
System.out.println("show2....");
}
public void show3() {
System.out.println("show3....");
}
}
然后创建Myadvice对象(对service增强)
public class MyAdvice {
public void advice1(){
System.out.println("前置增强方法.....");
}
public void advice2(){
System.out.println("后置增强方法.....");
}
}
随后创建代理对象。这里代理对象通过实现BeanPostProcessor的postProcessAfterInitialization方法进行增强处理
public class MockBeanPostProfessor implements BeanPostProcessor, ApplicationContextAware {
//要想获取Myadvice实例对象,就得用ApplicationContext的getBean方法来从Spring容器中获得
ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//先进行筛选
if(bean.getClass().getPackage().getName().equals("com.demo.service.impl")){//只要是这个包里的
//生成service对象的代理对象
Object beanProxy=Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(Object proxy, Method method,Object[] args)->{
//执行前置增强方法
MyAdvice myAdvice=applicationContext.getBean(MyAdvice.class);
myAdvice.advice1();
//执行service方法
Object result = method.invoke(bean, args);
//执行后置增强方法
myAdvice.advice2();
return result;
}
);
//返回增强结果
return beanProxy;
}
//反之如果不是这个包的,那就不是想要增强的对象,返回自身即可
return bean;
}
}
执行一下:
概念 | 单词 | 解释 |
---|---|---|
目标对象 | Target | 被增强的方法所属对象 |
代理对象 | Proxy | 对目标对象进行增强后的对象,客户端实际调用的对象 |
连接点 | Joinpoint | 目标对象中可以被增强的方法(基本都可以被增强) |
切入点 | Pointcut | 目标对象中可以被增强的方法 |
通知\增强 | Advice | 增强部分的代码逻辑 |
切面 | Aspect | 增强和切入点的组合 |
织入 | Weaving | 将增强和切入点的组合进行动态组合的过程 |
1.2.引入aop相关依赖
2.准备目标类和增强类并配置给Spring管理
3.配置切入点和切面
4.配置织入
1.引入aop相关依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
234.配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
//aop命名空间
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
//aop地址
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--目标类-->
<bean id="userService" class="com.demo.service.impl.UserServiceImpl"></bean>
<!--增强类-->
<bean id="myAdvice" class="com.demo.advice.MyAdvice"></bean>
<!--配置切点,在此之前要先去创建aop的命名空间(上面)-->
<aop:config>
<!--配置切点表达式,指定哪些方法被增强-->
<aop:pointcut id="Mypointcut" expression="execution(void com.demo.service.impl.UserServiceImpl.show1())"/>
<!--配置织入,将哪些切点与哪些增强进行结合-->
<aop:aspect ref="myAdvice">
<aop:before method="advice1" pointcut-ref="Mypointcut"></aop:before>
</aop:aspect>
</aop:config>
</beans>
执行
1.切点表达式
execution(【修饰符,可省略】返回类型 包名.类名.方法名(参数) )
返回类型、某一级包名、类名、方法名,可以使用*来表示任意
包名与类名之间使用… 表示该包及其子包下的类
参数可用…表示任意参数
2.通知类型
通知名称 | 配置方式 | 执行时机 |
---|---|---|
前置通知 | < aop:before > | 目标方法执行之前 |
后置通知 | < aop:after-returning > | 目标方法执行之后执行,若目标方法异常则不执行 |
环绕通知 | < aop:around > | 目标方法执行前后执行,若目标方法异常,环绕后方法不执行 |
异常通知 | < aop:after-throwing > | 目标方法抛出异常时执行 |
最终通知 | < aop:after > | 不管目标方法是否有异常,最终都会执行 |
其中就拿环绕通知做个例子:
//通知的环绕方法
public Object around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
System.out.println("前置增强方法.....");
//执行目标方法
Object proceed = proceedingJoinPoint.proceed();
System.out.println("后置增强方法.....");
return proceed;
}
<!--配置切点,在此之前要先去创建aop的命名空间-->
<aop:config>
<!--配置切点表达式,指定哪些方法被增强-->
<aop:pointcut id="Mypointcut" expression="execution(void com.demo.service.impl.UserServiceImpl.show1())"/>
<!--配置织入,将哪些切点与哪些增强进行结合-->
<aop:aspect ref="myAdvice">
<aop:around method="around" pointcut-ref="Mypointcut"/>
</aop:aspect>
</aop:config>
另外:除了aspect这种配置方式外,还可以通过advisor方式。不过多解释了。
相关配置:
<!--组件扫描-->
<context:component-scan base-package="com.demo"></context:component-scan>
<!--目标类-->
<bean id="userService" class="com.demo.service.impl.UserServiceImpl"></bean>
<!--增强类-->
<bean id="myAdvice" class="com.demo.advice.MyAdvice"></bean>
<!--开启aop自动代理!!!!-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
目标方法:
@Service("userService")
public class UserServiceImpl implements UserService {
public void show1() {
System.out.println("show1....");
}
public void show2() {
System.out.println("show2....");
}
public void show3() {
System.out.println("show3....");
}
}
增强方法:
@Component
@Aspect
public class MyAdvice {
@Before("execution(void com.demo.service.impl.UserServiceImpl.show1())")
public void advice1(){
System.out.println("前置增强方法.....");
}
}
@Component
@Aspect
public class MyAdvice {
//将切点表达式抽取出来
@Pointcut("execution(void com.demo.service.impl.UserServiceImpl.show1())")
public void myPointcut(){}
//使用切点表达式
@Before("MyAdvice.myPointcut()")
public void advice1(){
System.out.println("前置增强方法.....");
}
//其他。。没啥好说的,直接根据配置方式加上注解即可
}