Spring入门-Aop

Aware接口

在一些业务场景下,可能会遇到一些类需要获取到容器的一些信息,那就可以通过Aware接口来实现。
Aware有很多实现类
在这里插入图片描述
每一个子接口都提供了一个set方法,方法的参数就是容器的一些变量信息,我们可以在bean中声明相关的成员变量来接受这个参数,接收到这个参数后就可以获取到详细信息了。

定义AwareService类

实现多个接口

@Service
@PropertySource(value = "javaboy.properties")
public class AwareService implements BeanNameAware, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware {


    private String beanName;
    private  ResourceLoader resourceLoader;
    private Environment environment;



    public void output() throws IOException {
        System.out.println("beanName = " + beanName);
        //读取javaboy.txt
        Resource resource = resourceLoader.getResource("javaboy.txt");
        BufferedReader br = new BufferedReader(new InputStreamReader(resource.getInputStream()));
        String s = br.readLine();
        System.out.println("s = " + s);
        br.close();
        //读取properties的属性
        String property = environment.getProperty("javaboy.address");
        System.out.println("property = " + property);
    }


    /**
     * 获取bean的生成 工厂
     * @param beanFactory
     * @throws BeansException
     */
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {

    }

    /**
     * 获取bean的名字
     * @param s
     */
    public void setBeanName(String s) {
        this.beanName = s;
    }


    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

配置扫描类

@Configuration
@ComponentScan
public class JavaConfig {
}

获取信息

public class Main {


    public static void main(String[] args) throws IOException {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        AwareService bean = ctx.getBean(AwareService.class);

        bean.output();


    }

}
结果

在这里插入图片描述

Aop切面编程

Aop是Spring中核心特性之一,面向切面编程,就是在程序运行时,不改变程序源码的情况下,动态的增强方法,实现对业务功能扩充。如:

  1. 日志
  2. 事务
  3. 权限认证
  4. 数据库操作

    在Aop中,常见的概念:
概念说明
切点要添加代码的地方,叫做切点
通知通知就是向切点动态添加的代码
切面切点+通知
连接点切点的定义

Aop的实现

Aop实际上时基于Java动态代理来实现的。
Java中的动态代理有两种方式:

  • cglib
  • jdk
    cglib:如果要代理的对象,实现了某个接口,那么Spring Aop会使用jdk代理去创建代理对象。
    jdk:对于没有实现接口的对象,使用cglib代理。

动态代理

基于JDK的动态代理实现

  1. 定义一个计数器接口
public interface MyCalculator {

    int add(int a, int b);
}
  1. 定义计算机接口的实现
public class MyCalculatorImpl implements MyCalculator {
    public int add(int a, int b) {
        return a+b;
    }
}
  1. 定义代理类
public class CalculatorProxy {

    public static Object getInsetance(final MyCalculatorImpl myCalculator) {

        return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler() {
            /**
             *
             * @param proxy 代理对象
             * @param method 代理的方法
             * @param args 方法的参数
             * @return 方法的返回值
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                System.out.println(method.getName() + ":方法开始执行了");
                Object invoke = method.invoke(myCalculator, args);
                System.out.println(method.getName() + ":方法结束执行了");
                return invoke;
            }
        });
    }
}

Proxy.newProxyInstance接受三个参数,第一个时classloader,第二个是代理实现的接口,第三个是代理对象方法的处理器,基于反射动态对功能进行加强。

  • 结果测试
public class Main {
    public static void main(String[] args) {
        MyCalculatorImpl myCalculator = new MyCalculatorImpl();
        MyCalculator calculator = (MyCalculator) CalculatorProxy.getInsetance(myCalculator);
        int add = calculator.add(1, 5);
        System.out.println("add = " + add);
    }
}

在这里插入图片描述

五种通知

Aop的通知类型有5种:

  • 前置通知
  • 后置通知
  • 异常通知
  • 返回通知
  • 环绕通知
    通知就是对原有代码上进行功能加强。
    添加相关pom依赖
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.5</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7.M2</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.7.M2</version>
        </dependency>

定义切点切面

有两种方式,自定义注解和使用规则方式,使用规则比较常用。

自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
}

在需要拦截的方法上添加该注解

public class MyCalculatorImpl implements MyCalculator {
    @Action
    public int add(int a, int b) {
        return a+b;
    }
}

接下来,定义通知

@Component
@Aspect //表明这是个切面
public class LogAspect {


    /**
     * 前置通知
     *
     * @param joinpoint
     */
    @Before("@annotation(Action)")
    public void before(JoinPoint joinpoint) {
        String name = joinpoint.getSignature().getName();
        System.out.println(name + "方法执行开始...");
    }

    /**
     * 后置通知
     * @param joinPoint
     */
    @After("@annotation(Action)")
    public void after(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "方法执行结束...");
    }

    /**
     * 返回通知
     * @param joinpoint
     * @param r
     */
    @AfterReturning(value = "@annotation(Action)", returning = "r")
    public void returning(JoinPoint joinpoint, Integer r) {
        String name = joinpoint.getSignature().getName();
        System.out.println(name + "方法返回通知:" + r);
    }


    /**
     * 异常通知,当目标方法抛出异常时触发
     * @param joinPoint
     */

    @AfterThrowing(value = "@annotation(Action)",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e ) {
        String name = joinPoint.getSignature().getName();
        System.out.println(name+ "方法异常通知:" + e.getMessage());
    }

    /**
     * 环绕通知
     * @param pjp
     * @return
     */
    @Around("@annotation(Action)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        
        //这个方法类似method.invoke方法,可以在它前后添加日志
        Object proceed = pjp.proceed(new Object[] {1,7});
        return proceed;
    }
}

自动扫描,注入到bean中去

@Configuration
@ComponentScan
@EnableAspectJAutoProxy //开启自动代理,aop需要加上
public class JavaConfig {
}

测试类

    public static void main(String[] args) {
//        test1();
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        MyCalculator cau = ctx.getBean(MyCalculator.class);
        cau.add(1,4);
        cau.min(3,2);
    }

在这里插入图片描述

统一定义切点

定义一个切点方法,然后进行其他通知引用这个方法就可以,方便维护。

    @Pointcut("@annotation(Action)")
    public void pointcut() {

    }


    /**
     * 前置通知
     *
     * @param joinpoint
     */
    @Before("pointcut()")
    public void before(JoinPoint joinpoint) {
        String name = joinpoint.getSignature().getName();
        System.out.println(name + "方法执行开始...");
    }

非侵入式切点

侵入式是根据一定的匹配规则进行拦截。

    @Pointcut("execution(* com.bug.aop.service.*.*(..))")
    public void pointcut() {

    }

    /**
     * 前置通知
     *
     * @param joinpoint
     */
    @Before("pointcut()")
    public void before(JoinPoint joinpoint) {
        String name = joinpoint.getSignature().getName();
        System.out.println(name + "方法执行开始...");
    }

这种就不需要在使用action注解去指定哪个方法需要进行切入了。

Xml配置Aop

切面类中不加注解,使用xml来实现


    /**
     * 前置通知
     *
     * @param joinpoint
     */
    public void before(JoinPoint joinpoint) {
        String name = joinpoint.getSignature().getName();
        System.out.println(name + "方法执行开始...");
    }

xml中配置切点和切面信息

    <!-- aop配置-->
    <bean class="com.bug.aop.LogAspectXml" id="logAspectXml"></bean>
    <bean class="com.bug.aop.service.MyCalculatorImpl" id="myCalculator"></bean>

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.bug.aop.service.*.*(..))"/>
        <aop:aspect ref="logAspectXml">
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
            <aop:after-returning method="returning" pointcut-ref="pointcut" returning="r"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
            <aop:around method="around" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

定义类进行测试

    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        MyCalculator myCalculator = (MyCalculator) ctx.getBean("myCalculator");
        myCalculator.add(5,1);
    }

发现也实现了aop功能,没问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值