Spring学习笔记-第三天:Spring注解开发(2)/aop简介/aop底层实现/aop基于jdk的动态代理

注入普通属性(value)

注入普通属性时用@Value,括号内写注入的值,可以把值注入给普通属性变量
作用? @Value可以直接去Spring容器中找对应的key,若匹配成果,可以赋值给下面的字符串等

注:不需要导入bean,而是在xml配置文件中有context标签引入property-placeholder,导入.properties文件后就可以直接调用key

@Value("${jdbc.driver}")

@Scope注解 可以用来替代bean中的scope

@Scope("prototype")

@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestroy 是用在方法上标注该方法是Bean的销毁方法
这两个用于替代 init-method destroy-method

    @PostConstruct//在构造之后
    public  void  init(){
        System.out.println("对象初始化方法");
    }
    @PreDestroy//在销毁之前
    public void destroy(){
        System.out.println("销毁方法");
    }

Spring新注解

有些配置是原始注解没法替代的:
加载外部的配置文件(properties)
对非自定义的bean束手无策
组件扫描也没有对应注解。

还需要使用注解替代的配置如下:

  • 非自定义的Bean的配置
  • 加载properties文件的配置
  • 组件扫描的配置
  • 引入其他文件

新注解

  • @Configuration 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
  • @ComponentScan 用于指定Spring初始化容器时要扫码的包,用于替代组件扫描的配置
  • @Bean 使用在方法上,标注该方法的返回值存储到Spring容器中
  • @PropertySource 用于加载配置文件
  • @Import 用于引入其他文件

代码实现替代xml配置文件

替代思想:用一个核心配置的类,用于替代xml文件-创建一个config包,设置SpringConfiguration
首先加入@Configuration注解,标志该类是SPring的核心配置类
首先替代扫描

<context:component-scan base-package="com.itheima"/>

换成

@ComponentScan("com.itheima")

然后替代外部jar包xml配置

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

首先换成类似替代

@Bean("dataSource") //Spring会将当前方法的返回值以指定名称存储到Spring容器当中
    public DataSource getDataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/bookstore");
        dataSource.setUser("root");
        dataSource.setPassword("123");
        return dataSource;
    }

但当前xml配置中是外部读取的,而当前Config是设定好的,因此我们也要加载外部配置文件

//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")

加载完毕后,对数据进行替换,但不能直接set替换,否则直接以字符串形式处理,不能解析
此时可以设定变量,通过Value注解进行数据读取

    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource") //Spring会将当前方法的返回值以指定名称存储到Spring容器当中
    public DataSource getDataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);
        return dataSource;
    }

@Import注解,也可以在config中建多个相关配置,再引入,如再创建数据源相关配置

//<import resource=""/>
@Import(DataSourceConfiguration.class)

也可以加载多个,内部相当于数组,用逗号分隔
同时需要修改ApplicationContext,不然仍加载xml

    //ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
        ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfiguration.class);
     

后期用SpringBoot后基本全注解。

Spring整合Junit

在测试类中,每个测试方法都有以下代码:

ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = app.getBean(UserService.class);

首先获得应用上下文对象,然后获得对应的被测试的对象

解决思路

  • 让SpringJunit负责创建Spring容器,但需要将配置文件的名称告诉它
  • 将需要进行测试Bean直接在测试类中进行注入,即用谁注入谁

Spring集成Junit步骤

  1. 导入Spring集成Junit的坐标
  2. 使用@Runwith注解替换掉原来的运行期
  3. 使用@ContextConfiguration指定配置文件或配置类
  4. 使用@Autowired注入需要进行测试的对象
  5. 创建测试方法进行测试

代码实现

  1. 导入坐标
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
  1. 编写测试类
    使用@Runwith注解替换掉原来的运行期
    使用@ContextConfiguration指定配置文件或配置类
    使用@Autowired注入需要进行测试的对象
    创建测试方法进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringJunitTest {
    @Autowired
    private UserService userService;
    @Test
    public void test1(){
        userService.save();
    }
}

全注解方式与xml方式区别

//@ContextConfiguration("classpath:applicationContext.xml")
@ContextConfiguration(classes = {SpringConfiguration.class})

AOP

Spring的AOP简介

面向切面编程:通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
动态代理:是在不修改代码的情况下对目标方法进行相应的增强
动态代理作用:实现程序功能间的松耦合,约等于AOP的作用,但Spring不用再写动态代理了(封装)
AOP是OOP的延续。

AOP的作用及其优势

  • 作用:在程序运行期间,在不修改代码的情况下对方法进行功能增强
  • 优势:减少重复代码,提高开发效率,便于维护

AOP的底层实现

AOP的底层是通过Spring提供的动态代理技术实现的,在运行期,Spring通过动态代理技术动态地生成代理对象,代理对象方法执行时进行增强功能的介入,再去调用目标对象的方法,从而完成功能的增强

AOP的动态代理技术

常用的动态代理技术

  • JDK代理:基于接口的动态代理技术
  • cglib代理:基于父级的动态代理技术

基于jdk代理
jdk内部要有接口(interface),并设计其实现,并设计出相应的增强函数,之后在proxyTest中进行配置
Proxy.newProxyInstance是目标类加载器,因此需要导入目标对象Target
同时获得增强对象
然后第一个参数获得目标对象的类加载器
然后第二个参数获得目标对象相同的接口字节码对象数组
第三个参数是个借口,new一个InvocationHandler

    public static void main(String[] args) {
        //目标对象
        final Target target=new Target();

        //获得增强对象
        Advice advice=new Advice();

        //返回值 就是动态生成的代理对象
        TargetInterface proxy=(TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), //目标对象的类加载器
                target.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组
                new InvocationHandler(){
                    //调用代理对象的任何方法,实质执行的都是invoke方法
                                        //proxy-代理对象 method-当前目标方法的对象 args-传递参数
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //执行目标方法
                        advice.before();//前置增强
                        Object invoke = method.invoke(target, args);
                        advice.afterReturning();//后置增强
                        return invoke;

                    }
                }
        );
        //调用代理对象的方法
        proxy.save();
    }

jdk动态代理是自动生成的,但要知道原理。

posted @ 2021-04-29 22:48  东风应笑我  阅读(61)  评论(0)    收藏  举报