Spring框架(三)


Bean管理(注解开发)

采用纯注解开发

看一下项目目录

  • SpringConfigure配置类
package com.yang.configure;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.yang")
public class SpringConfigure {
}
  • APP测试类
public class APP {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfigure.class);
        BookDao bookDao1 = ctx.getBean(BookDao.class);
        BookDao bookDao2 = ctx.getBean(BookDao.class);
        System.out.println(bookDao1);
        System.out.println(bookDao2);
        ctx.close();
    }
}

测试下来没有问题

  • 和注解开发一样在bean后面加个scope即可
package com.yang.doc.impl;


import com.yang.doc.BookDao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;


@Repository
@Scope("prototype")
public class BookDaoImpl implements BookDao {

    @Override
    public void save() {
        System.out.println("BookDao save......");
    }
}
  • 运行结果发现两个地址不一样

  • 销毁创建bean,只要添加两个注解即可
@Repository
@Scope("prototype")
public class BookDaoImpl implements BookDao {

    @Override
    public void save() {
        System.out.println("BookDao save......");
    }
    //@PostConstruct设置bean的初始化方法,与函数名称无关
    @PostConstruct
    public void init() {
        System.out.println("init ...");
    }
    //@PreDestroy设置bean的销毁方法,与函数名称无关
    @PreDestroy
    public void destroy() {
        System.out.println("destroy ...");
    }
}

依赖注入(注解开发)

使用@Autowired注解开启自动装配模式(按类型)

@Service
public class BookServiceImpl implements BookService {
    //@Autowired:注入引用类型,自动装配模式,默认按类型装配
    @Autowired
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

可以不用setter函数:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法。这其实有悖于IOC理念,你已经不提供入口了,但它简单啊!

使用@Qualifier注解指定要装配的bean名称

解决IOC容器中同类型Bean有多个装配哪一个的问题

@Service
public class BookServiceImpl implements BookService {
    //@Autowired:注入引用类型,自动装配模式,默认按类型装配
    @Autowired
    //@Qualifier:自动装配bean时按bean名称装配
    @Qualifier("bookDao")
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

@Qualifier注解无法单独使用,必须配合@Autowired注解使用

使用@Value实现简单类型注入

@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    //@Value:注入简单类型(无需提供set方法)
    @Value("${name}")
    private String name;

    public void save() {
        System.out.println("book dao save ..." + name);
    }
}

@Value注解中使用${name}从属性文件中读取name值,那么就需要在配置类或者配置文件中加载属性文件。

@Configuration
@ComponentScan("com.itheima")
//@PropertySource加载properties配置文件
@PropertySource({"classpath:jdbc.properties"}) //{}可以省略不写
public class SpringConfig {
}

@PropertySource()中加载多文件请使用数组格式配置,不允许使用通配符*

注解开发管理第三方Bean:

在pom安装依赖durid

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.16</version>
</dependency>
  • 单独定义配置类(在configure文件夹下,导入Jdbc配置文件)
public class JdbcConfig {
    //@Bean:表示当前方法的返回值是一个bean对象,添加到IOC容器中
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}
  • 将独立的配置类加入核心配置

@Import注解导入式

@Configuration
@ComponentScan("com.itheima")
//@Import:导入配置信息
@Import({JdbcConfig.class})
public class SpringConfig {
}

注解开发为第三方Bean注入资源

配置类中注入简单类型,注入引用类型

  • 简单类型(也是用value进行注入)
public class JdbcConfig {
    //1.定义一个方法获得要管理的对象
    @Value("com.mysql.jdbc.Driver")
    private String driver;
    @Value("jdbc:mysql://localhost:3306/spring_db")
    private String url;
    @Value("root")
    private String userName;
    @Value("root")
    private String password;
    //2.@Bean:表示当前方法的返回值是一个bean对象,添加到IOC容器中
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}
  • 引用类型依赖注入
//Spring会自动从IOC容器中找到BookDao对象赋值给参数bookDao变量,如果没有就会报错。
@Bean 
public DataSource dataSource(BookDao bookDao){
    System.out.println(bookDao);
    DruidDataSource ds = new DruidDataSource();
    ds.setDriverClassName(driver);
    ds.setUrl(url);
    ds.setUsername(userName);
    ds.setPassword(password);
    return ds;
}

AOP

AOP简介和作用

  • AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构
    • OOP(Object Oriented Programming)面向对象编程
  • 作用:在不惊动原始设计的基础上为其进行功能增强。简单的说就是在不改变方法源代码的基础上对方法进行功能增强。
  • Spring理念:无入侵式/无侵入式

AOP中的核心概念:

  • 连接点(JoinPoint):正在执行的方法。
  • 切入点(Pointcut):进行功能增强了的方法,连接点包含切入点。
  • 在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
    • 一个具体方法
    • 匹配多个方法:
  • 通知(Advice):在切入点前后执行的操作,也就是增强的共性功能
  • 在SpringAOP中,功能最终以方法的形式呈现
  • 通知类:通知方法所在的类叫做通知类
  • 切面(Aspect):描述通知与切入点的对应关系,也就是哪些通知方法对应哪些切入点方法。

AOP入门案例

这里使用纯注解开发

  • 【第一步】导入aop相关坐标
<dependencies>
    <!--spring核心依赖,会将spring-aop传递进来-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <!--切入点表达式依赖,目的是找到切入点方法,也就是找到要增强的方法-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
</dependencies>
  • 【第二步】定义dao接口与实现类
public interface BookDao {
    public void save();
    public void update();
}

@Repository
public class BookDaoImpl implements BookDao {

    public void save() {
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save ...");
    }
    public void update(){
        System.out.println("book dao update ...");
    }
}
  • 【第三步】定义通知类,制作通知方法
//通知类必须配置成Spring管理的bean
@Component
public class MyAdvice {
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}
  • 【第四步】定义切入点表达式、配置切面(绑定切入点与通知关系)
//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类类
@Aspect
public class MyAdvice {
    //设置切入点,@Pointcut注解要求配置在方法上方
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}

    //设置在切入点pt()的前面运行当前操作(前置通知)
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}
  • 【第五步】在配置类中进行Spring注解包扫描和开启AOP功能
@Configuration
@ComponentScan("com.itheima")
//开启注解开发AOP功能
@EnableAspectJAutoProxy
public class SpringConfig {
}
  • 测试类
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.update();
    }
}
  • 结果

常见的通知

前置通知 方法执行前调用 对应注解 @Before

设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行

后置通知 方法执行后调用 对应注解 @After

设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行

返回通知 方法返回后调用 对应注解 @AfterReturning

设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行

异常通知 方法出现异常调用 对应注解 @AfterThrowing

设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行

环绕通知 动态代理、手动推荐方法运行 对应注解 @Around

设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行

AOP工作流程

  1. Spring容器启动
  2. 读取所有切面配置中的切入点
  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
  • 匹配失败,创建原始对象
  • 匹配成功,创建原始对象(目标对象)的代理对象
  1. 获取bean执行方法
  • 获取的bean是原始对象时,调用方法并执行,完成操作
  • 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

基础概念:

目标对象(Target):被代理的对象,也叫原始对象,该对象中的方法没有任何功能增强。
代理对象(Proxy):代理后生成的对象,由Spring帮我们创建代理对象。


文章作者: 小小星仔
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小小星仔 !
评论
  目录