其他分享
首页 > 其他分享> > day42_Spring02

day42_Spring02

作者:互联网

今日学习目标

一、基于注解的IOC和DI

1. 快速入门(重点)

需求描述

需求分析

  1. 准备工作:创建Maven项目,导入依赖坐标

  2. 编写代码并注解配置:

    编写dao层、service层代码,使用注解@Component配置bean:代替xml里的bean标签

    使用注解@Autowired依赖注入:代替xml里的propertyconstructor-arg标签

  3. 在xml配置文件中开启组件扫描

  4. 测试

需求实现

1) 准备工作
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
2) 编写代码,并注解配置
package com.itheima.dao;

public interface UserDao {
    void add();
}
package com.itheima.dao.impl;

import com.itheima.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

@Component
public class UserDaoImpl implements UserDao {
    public void add() {
        System.out.println("调用了UserDaoImpl的add方法~!~");
    }
}


package com.itheima.service;

public interface UserService {
    void add();
}

package com.itheima.service.impl;

import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/*
    需求:
        在UserServiceImpl里面调用UserDaoImpl的add方法
    分析:
        1. 把这两个类交给spring管理,让spring创建这两个类的对象
        2. 在UserServiceImpl里面注入UserDaoImpl 的对象
        3. 使用对象来调用方法
     步骤:
        1. 在UserServiceImpl和UserDaoImpl身上打注解 :  @Component
        2. 在UserServiceImpl里面定义属性,private UserDao userDao;
        3. 在属性身上打注解:  @AutoWired
        4. 在xml里面打开扫描的开关,这样spring才能知道哪些类要创建对象,里面的什么属性要注入!
 */
@Component 
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDaoImpl;

    public void add() {
        System.out.println("调用了UserServiceImpl的add方法~!");
        userDaoImpl.add();
    }

}

3) 开启组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


    <!--
        1. 告诉spring要扫描指定的包,因为这个包下有些类要创建对象
        context:component-scan : 用来指定扫描组件的标签
            base-package : 表示要扫描哪个包
                a. 可以写一个总的包名
                b. 可以写具体的包名,可以写多!使用 空格,逗号,分号,tab 分隔!
    -->
    <!--<context:component-scan base-package="com.itheima.dao.impl com.itheima.service.impl"/>-->
    <context:component-scan base-package="com.itheima"/>
</beans>
4) 功能测试
package com.itheima.test;

import com.itheima.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUserServiceImpl {

    @Test
    public void testAdd(){

        //1. 创建工厂
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //2. 问工厂要对象
        UserService us = context.getBean(UserService.class);

        //3. 调用方法
        us.add();

    }
}

步骤小结

  1. 导入依赖

  2. 定义接口和实现类(dao 和 service)

  3. 在实现类上面打上注解 @Component

  4. 在属性上面打上注解@AutoWired

  5. 在applicationContext.xml里面打开扫描的开关

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

2. 注解使用详解

2.1 开启组件扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


    <!--
        1. 告诉spring要扫描指定的包,因为这个包下有些类要创建对象
        context:component-scan : 用来指定扫描组件的标签
            base-package : 表示要扫描哪个包
                a. 可以写一个总的包名
                b. 可以写具体的包名,可以写多!使用 空格,逗号,分号,tab 分隔!
    -->
    <!--<context:component-scan base-package="com.itheima.dao.impl com.itheima.service.impl"/>-->
    <context:component-scan base-package="com.itheima"/>
</beans>

2.2 声明bean的注解【IOC】

简介
注解 说明
@Component 用在类上,相当于bean标签
@Controller 用在web层类上,配置一个bean(是@Component的衍生注解)
@Service 用在service层类上,配置一个bean(是@Component的衍生注解)
@Repository 用在dao层类上,配置一个bean(是@Component的衍生注解)
示例
@Repository("userDao")
public class UserDaoImpl implements UserDao{
}
@Service("userService")
public class UserServiceImpl implements UserService{
}
@Controller
public class UserController{}

2.3 配置bean的注解 【IOC】

注解 说明
@Scope 相当于bean标签的scope属性
@PostConstruct 相当于bean标签的init-method属性
@PreDestroy 相当于bean标签的destory-method属性
配置bean的作用范围:
@Scope("prototype")
@Service("userService")
public class UserServiceImpl implements UserService{
    //...
}
配置bean生命周期的方法
package com.itheima.service.impl;

import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/*
    需求:
        在UserServiceImpl里面调用UserDaoImpl的add方法
    分析:
        1. 把这两个类交给spring管理,让spring创建这两个类的对象
        2. 在UserServiceImpl里面注入UserDaoImpl 的对象
        3. 使用对象来调用方法
     步骤:
        1. 在UserServiceImpl和UserDaoImpl身上打注解 :  @Component
        2. 在UserServiceImpl里面定义属性,private UserDao userDao;
        3. 在属性身上打注解:  @AutoWired
        4. 在xml里面打开扫描的开关,这样spring才能知道哪些类要创建对象,里面的什么属性要注入!
 */


/*
    IOC的注解:
        @Component : 是通用的注解
            @Controller :针对web层
            @Service : 针对service层
            @Repository :针对dao层

            属性:
                value :  用来指定id值,如果不指定,那么将会把类名(首字母小写)作为id值!

        @Scope: 用来配置单例或者多例
            singleton: 单例, 默认就是单例
            prototype: 多例
        @PostConstruct: 创建对象的时候,调用指定的方法
        @PreDestroy: 销毁对象的时候调用指定的方法

 */
//@Component //组件
@Service("us")
@Scope("prototype")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDaoImpl;

    public void add() {
        System.out.println("调用了UserServiceImpl的add方法~!");
        userDaoImpl.add();
    }

    //=========================================
    //对象创建完毕,就执行这个方法
    @PostConstruct
    public void init(){
        System.out.println("调用了UserServiceImpl的init方法~!");
    }

    //对象销毁的时候,就执行这个方法! 只有单例才会走这个方法
    @PreDestroy
    public void destroy(){
        System.out.println("调用了UserServiceImpl的destroy方法~!");
    }
}

2.4 依赖注入的注解【DI】

注解 说明
@Autowired 相当于property标签的ref 注入对象
@Qualifier 结合@Autowired使用,用于根据名称(标识符)注入依赖
@Resource 相当于@Autowired + @Qualifier
@Value 相当于property标签的value ,注入普通 的属性
注入bean对象

绝大多数情况下,只要使用@Autowired注解注入即可

使用注解注入时,不需要set方法了

package com.itehima.dao;

public interface UserDao {
    void add();
}
package com.itheima.dao.impl;

import com.itheima.dao.UserDao;
import org.springframework.stereotype.Component;

@Repository
public class UserDaoImpl implements UserDao {
    public void add() {
        System.out.println("调用了UserdaoImpl的add方法~!~");
    }
}

package com.itehima.service;

public interface UserService {
    void add();
}
package com.itheima.service.impl;

import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

@Service("us02")
public class UserServiceImpl02 implements UserService {

    /*

        @Autowired :自动注入
            1. 按照的属性类型去找spring的工厂里面找对象,找到对象(只有一个)就注入进来
            2. 如果在spring的工厂里面,有多个对象满足这种类型,
                2.1 会拿着属性的名字当成id ,再去找对象,如果有匹配的就注入。
                2.2 如果还没有找到匹配的,就会报错!
        @Qualifier : 用于给@Autowired指定id值。告诉它用这个id去找对象注入
        @Resource :  等价于@Autowired + @Qualifier
            1. 按照id去找对象注入

         以上三个注解,就是用注入对象的,最常用的是@Autowired,而且95%的场景下,是只有一种实现类\

         @Value : 用来注入普通的数据
            1. 主要是用来注入properties里面的数据
            2. @Value("${properties里面的KEY}") , 要先导入进来properties的文件才可以注入
     */
    @Autowired
    private UserDao userDaoImpl02;

    @Autowired
    @Qualifier("userDaoImpl02")
    private UserDao abc;

    @Resource(name = "userDaoImpl02")
    private UserDao cba;

    @Value("深圳")
    private String address;

    public void add() {
        System.out.println("调用了UserServiceImpl02的add方法~!" + address);
        //userDaoImpl02.add();
        //abc.add();
        cba.add();
    }
}


package com.itheima.test;

import com.itheima.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUserServiceImpl02 {

    @Test
    public void testAdd(){

        //1. 创建工厂
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //2. 问工厂要对象
        UserService us = (UserService) context.getBean("us02");


        //3. 调用方法
        us.add();

    }
}


注入普通值
@Service
public class UserServiceImpl02 implements UserService{
    
    @Value("深圳")
    private String address;
    
    //...
}

小结

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

二、注解方式CURD练习

需求描述

需求分析

需求实现

 <dependencies>
        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <!-- c3p0连接池(也可以用其它连接池) -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

        <!-- DBUtils工具包 -->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>

        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>

        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
     
         <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
    </dependencies>

JavaBean

package com.itheima.bean;

import lombok.Data;

@Data
public class Account {
    private int id;
    private String name;
    private int money;
}

dao层代码

package com.itheima.dao;

import com.itheima.bean.Account;

import java.util.List;

public interface AccountDao {
    int add(Account account) throws Exception;
    int delete(int id) throws Exception;
    int update(Account account) throws Exception;
    Account findById(int id) throws Exception;
    List<Account> findAll() throws Exception;
}


package com.itheima.dao.impl;

import com.itheima.bean.Account;
import com.itheima.dao.AccountDao;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.sql.SQLException;
import java.util.List;

/*
    1. 把这个类交给spring管理
    2. 让spring注入QueryRunner对象
 */
@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner runner;

    public int add(Account account) throws SQLException {
        String sql = "insert into t_account values(null , ? , ?)";
        return runner.update(sql , account.getName(),account.getMoney());
    }

    public int delete(int id) throws SQLException {
        String sql = "delete from t_account where id = ?";
        return runner.update(sql , id);
    }

    public int update(Account account) throws SQLException {
        String sql = "update t_account set name = ?, money = ? where id = ?";
        return runner.update(sql , account.getName() ,account.getMoney() ,account.getId());
    }

    public Account findById(int id) throws SQLException {
        String sql = "select * from t_account where id = ?";
        return runner.query(sql , new BeanHandler<Account>( Account.class),  id);
    }

    public List<Account> findAll() throws SQLException {
        String sql = "select * from t_account ";
        return runner.query(sql , new BeanListHandler<Account>( Account.class));
    }
}

service层代码

    package com.itheima.service;
    
    import com.itheima.bean.Account;
    
    import java.util.List;
    
    public interface AccountService {
        int add(Account account) throws Exception;
        int delete(int id) throws Exception;
        int update(Account account) throws Exception;
        Account findById(int id) throws Exception;
        List<Account> findAll() throws Exception;
    }

package com.itheima.service.impl;

import com.itheima.bean.Account;
import com.itheima.dao.AccountDao;
import com.itheima.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.List;

/*
    1. 把这个类交给spring管理
        打注解:  @Service
    2. 让spring把dao的对象给注入进来

        @Autowired
        private AccountDao dao;
 */
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao dao;

    public int add(Account account) throws SQLException {
        return dao.add(account);
    }

    public int delete(int id) throws SQLException {
        return dao.delete(id);
    }

    public int update(Account account) throws SQLException {
        return dao.update(account);
    }

    public Account findById(int id) throws SQLException {
        return dao.findById(id);
    }

    public List<Account> findAll() throws SQLException {
        return dao.findAll();
    }
}

提供配置

db.driverClass=com.mysql.jdbc.Driver
db.jdbcUrl=jdbc:mysql://localhost:3306/day41_spring
#  如果是windows系统,那么使用${username} 那么取到的不是这里的root 而是计算机的用户名
#  如果是mac系统,那么使用${user} 那么取到的不是这里的root, 而是计算机的用户名!
db.user=root
db.password=root
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--1. 打开扫描的开关-->
    <context:component-scan base-package="com.itheima"/>

    <!--2. 把QueryRunner交给spring管理-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="ds"/>
    </bean>
    <!--3. 把DataSource交给spring管理-->
    <context:property-placeholder location="db.properties"/>
    <bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${db.driverClass}"/>
        <property name="jdbcUrl" value="${db.jdbcUrl}"/>
        <property name="user" value="${db.user}"/>
        <property name="password" value="${db.password}"/>
    </bean>

</beans>

功能测试

package com.itheima.test;

import com.itheima.bean.Account;
import com.itheima.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.sql.SQLException;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAccountServiceImpl {

    @Autowired
    private AccountService as;

    @Test
    public void testAdd() throws SQLException {
        Account a = new Account();
        a.setName("zhangsan");
        a.setMoney(10);

        as.add(a);
    }

    @Test
    public void testDelete() throws SQLException {
       as.delete(5);
    }

    @Test
    public void testUpdate() throws SQLException {
        //1. 先查询
        Account a = as.findById(3);
        a.setMoney(66666);

        //2. 再修改
        as.update(a);
    }

    @Test
    public void testFindAll() throws SQLException {
        System.out.println(as.findAll());
    }
}

小结

  1. 导入依赖
  2. 定义dao接口和service 接口 各自的实现
  3. 使用注解托管dao的实现类和service的实现类 @Repository@Service
  4. 不要忘记了在applicationContext.xml 打开开关
  5. 在applicationContext.xml里面托管QueryRunner,这样才能给Dao里面注入
  6. 在applicationContext.xml里面托管ComboPooledDataSource 这样才能给QueryRunner注入

三、纯注解开发IOC和DI

​ 在上边的CURD练习中,仍然有部分配置需要使用applicationContext.xml,那么能不能使用注解替换掉所有的xml呢?Spring提供了一些新注解,可以达到这个目标。

请注意:Spring提供的这部分新注解,并非为了完全代替掉XML,只是提供了另外一种配置方案

注解简介

注解 说明
@Configuration 被此注解标记的类,是配置类 等同于applicationContext.xml
@ComponentScan 用在配置类上,开启注解扫描。使用basePackage属性指定扫描的包
@PropertySource 用在配置类上,加载properties文件。使用value属性指定properties文件路径
@Import 用在配置类上,引入子配置类。用value属性指定子配置类的Class
@Bean 用在配置类的方法上,把返回值声明为一个bean。用name/value属性指定bean的id

注解详解

<dependencies>
        <!-- c3p0连接池(也可以用其它连接池) -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!-- DBUtils工具包 -->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>
        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
    </dependencies>

1 @Configuration配置类

/*
    @Configuration:
        1. 表示这个类是一个核心配置类
        2. 它的作用等价于以前的applicationContext.xml
        3. @Configuration 是从@Component注解衍生出来的,所以这个类也会被spring管理起来(创建对象)
 */
@Configuration
public class AppConfig {
}

2 配置类上的注解

package com.itheima.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;

/*
    @Configuration:
        1. 表示这个类是一个核心配置类
        2. 它的作用等价于以前的applicationContext.xml
        3. @Configuration 是从@Component注解衍生出来的,所以这个类也会被spring管理起来(创建对象)
    @ComponentScan :
        1. 用来指定扫描包,等价于 <context:component-scan base-package=""/>
    @PropertyScource:
        1. 用来导入外部的properties文件,等价于<context:property-placeholder location=""/>
        2. 导入这个properties文件的时候,如果有错(1. 找不到这个文件,2.这个文件无法打开,)可以在
            名字的前面加上 classpath:
        3. 要想把properties里面的内容注入进来,就需要定义好对应的属性,然后使用@Value来注入值
            @Value("${KEY}")
     @Import:
        1. 可以用来导入子配置类
 */
@Configuration
//@ComponentScan("com.itheima")
//@PropertySource("db.properties")
@Import(AppConfig01.class) //导入子配置类
public class AppConfig {
    @Value("${db.driverClass}")
    private String driverClass;

    @Value("${db.jdbcUrl}")
    private String jdbcUrl;

    @Value("${db.user}")
    private String user;

    @Value("${db.password}")
    private String password;

    public void show(){
        System.out.println("driverClass = " + driverClass);
        System.out.println("jdbcUrl = " + jdbcUrl);
        System.out.println("user = " + user);
        System.out.println("password = " + password);
    }

    //==============================以下代码是说明@Bean=============================

    /*
        @Bean
            1. 打在方法身上,spring会来调用这个方法
            2. spring会把这个方法的返回值管理起来,丢到spring的工厂里面去
            3. 可以在@Bean的value属性里面设置对象的id,如果不设置,那么将会使用方法的名字作为id值
     */

    //1. 把QueryRunner对象交给spring管理,使用方法名字作为id值
    @Bean
    public QueryRunner runner01(){
        return new QueryRunner();
    }

    // 2. 把QueryRunner对象交给spring管理,使用属性来指定id值
    @Bean("run02")
    public QueryRunner runner02(){
        return new QueryRunner();
    }

    /*
        3.使用@Bean注解标记的方法,方法内部需要用到spring工厂(容器)里面的某一个对象,怎么办?
            3.1 此时可以在方法的参数上,写上想要的对象参数即可
                a. 其实它就是在方法参数的前面,隐藏着一个注解: @Autowired
            3.2 但是也要考虑出现极端的情况:如果在spring的容器里面,有多个对象满足这个参数的类型,咋办?
                a. 搭配使用@Qualifier ,指定id值。
                b. 投机取巧,把方法的参数名字,写成id的名字即可

     */
    @Bean
    public QueryRunner runner03(@Qualifier("dataSource") DataSource ds){
        System.out.println("ds===" + ds);
        return new QueryRunner(ds);
    }

    @Bean
    public QueryRunner runner04(DataSource dataSource2){
        System.out.println("dataSource2===" + dataSource2);
        return new QueryRunner(dataSource2);
    }

    @Bean
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
   @Bean
    public DataSource dataSource2() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
}

package com.itheima.config;

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

@ComponentScan("com.itheima")
@PropertySource("db.properties")
public class AppConfig01 {
}

3 @Bean声明bean

1) @Bean定义bean
@Configuration
@ComponentScan("com.itheima")
public class AppConfig {
    
    ...

    //==============================以下代码是说明@Bean=============================

    /*
        @Bean
            1. 打在方法身上,spring会来调用这个方法
            2. spring会把这个方法的返回值管理起来,丢到spring的工厂里面去
            3. 可以在@Bean的value属性里面设置对象的id,如果不设置,那么将会使用方法的名字作为id值
     */

    //1. 把QueryRunner对象交给spring管理,使用方法名字作为id值
    @Bean
    public QueryRunner runner01(){
        return new QueryRunner();
    }

    // 2. 把QueryRunner对象交给spring管理,使用属性来指定id值
    @Bean("run02")
    public QueryRunner runner02(){
        return new QueryRunner();
    }

    /*
        3.使用@Bean注解标记的方法,方法内部需要用到spring工厂(容器)里面的某一个对象,怎么办?
            3.1 此时可以在方法的参数上,写上想要的对象参数即可
                a. 其实它就是在方法参数的前面,隐藏着一个注解: @Autowired
            3.2 但是也要考虑出现极端的情况:如果在spring的容器里面,有多个对象满足这个参数的类型,咋办?
                a. 搭配使用@Qualifier ,指定id值。
                b. 投机取巧,把方法的参数名字,写成id的名字即可

     */
    @Bean
    public QueryRunner runner03(@Qualifier("dataSource") DataSource ds){
        System.out.println("ds===" + ds);
        return new QueryRunner(ds);
    }

    @Bean
    public QueryRunner runner04(DataSource dataSource2){
        System.out.println("dataSource2===" + dataSource2);
        return new QueryRunner(dataSource2);
    }

    @Bean
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
   @Bean
    public DataSource dataSource2() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
    

}
2) @Bean的依赖注入
3) 完整代码
package com.itheima.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;

/*
    @Configuration:
        1. 表示这个类是一个核心配置类
        2. 它的作用等价于以前的applicationContext.xml
        3. @Configuration 是从@Component注解衍生出来的,所以这个类也会被spring管理起来(创建对象)
    @ComponentScan :
        1. 用来指定扫描包,等价于 <context:component-scan base-package=""/>
    @PropertyScource:
        1. 用来导入外部的properties文件,等价于<context:property-placeholder location=""/>
        2. 导入这个properties文件的时候,如果有错(1. 找不到这个文件,2.这个文件无法打开,)可以在
            名字的前面加上 classpath:
        3. 要想把properties里面的内容注入进来,就需要定义好对应的属性,然后使用@Value来注入值
            @Value("${KEY}")
     @Import:
        1. 可以用来导入子配置类
 */
@Configuration
//@ComponentScan("com.itheima")
//@PropertySource("db.properties")
@Import(AppConfig01.class) //导入子配置类
public class AppConfig {
    @Value("${db.driverClass}")
    private String driverClass;

    @Value("${db.jdbcUrl}")
    private String jdbcUrl;

    @Value("${db.user}")
    private String user;

    @Value("${db.password}")
    private String password;

    public void show(){
        System.out.println("driverClass = " + driverClass);
        System.out.println("jdbcUrl = " + jdbcUrl);
        System.out.println("user = " + user);
        System.out.println("password = " + password);
    }

    //==============================以下代码是说明@Bean=============================

    /*
        @Bean
            1. 打在方法身上,spring会来调用这个方法
            2. spring会把这个方法的返回值管理起来,丢到spring的工厂里面去
            3. 可以在@Bean的value属性里面设置对象的id,如果不设置,那么将会使用方法的名字作为id值
     */

    //1. 把QueryRunner对象交给spring管理,使用方法名字作为id值
    @Bean
    public QueryRunner runner01(){
        return new QueryRunner();
    }

    // 2. 把QueryRunner对象交给spring管理,使用属性来指定id值
    @Bean("run02")
    public QueryRunner runner02(){
        return new QueryRunner();
    }

    /*
        3.使用@Bean注解标记的方法,方法内部需要用到spring工厂(容器)里面的某一个对象,怎么办?
            3.1 此时可以在方法的参数上,写上想要的对象参数即可
                a. 其实它就是在方法参数的前面,隐藏着一个注解: @Autowired
            3.2 但是也要考虑出现极端的情况:如果在spring的容器里面,有多个对象满足这个参数的类型,咋办?
                a. 搭配使用@Qualifier ,指定id值。
                b. 投机取巧,把方法的参数名字,写成id的名字即可

     */
    @Bean
    public QueryRunner runner03(@Qualifier("dataSource") DataSource ds){
        System.out.println("ds===" + ds);
        return new QueryRunner(ds);
    }

    @Bean
    public QueryRunner runner04(DataSource dataSource2){
        System.out.println("dataSource2===" + dataSource2);
        return new QueryRunner(dataSource2);
    }

    @Bean
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
   @Bean
    public DataSource dataSource2() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
}

小结

四、纯注解方式CURD练习

需求描述

需求实现

<dependencies>
        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <!-- c3p0连接池(也可以用其它连接池) -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

        <!-- DBUtils工具包 -->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>

        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>

        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
    </dependencies>
package com.itheima.bean;

import lombok.Data;

@Data
public class Account {
    private int id;
    private String name;
    private int money;
}

package com.itheima.dao;

import com.itheima.bean.Account;

import java.util.List;

public interface AccountDao {
    int add(Account account) throws Exception;
    int delete(int id) throws Exception;
    int update(Account account) throws Exception;
    Account findById(int id) throws Exception;
    List<Account> findAll() throws Exception;
}

package com.itheima.dao.impl;

import com.itheima.bean.Account;
import com.itheima.dao.AccountDao;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.sql.SQLException;
import java.util.List;

/*
    1. 把这个类交给spring管理
    2. 让spring注入QueryRunner对象
 */
@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner runner;

    public int add(Account account) throws SQLException {
        String sql = "insert into t_account values(null , ? , ?)";
        return runner.update(sql , account.getName(),account.getMoney());
    }

    public int delete(int id) throws SQLException {
        String sql = "delete from t_account where id = ?";
        return runner.update(sql , id);
    }

    public int update(Account account) throws SQLException {
        String sql = "update t_account set name = ?, money = ? where id = ?";
        return runner.update(sql , account.getName() ,account.getMoney() ,account.getId());
    }

    public Account findById(int id) throws SQLException {
        String sql = "select * from t_account where id = ?";
        return runner.query(sql , new BeanHandler<Account>( Account.class),  id);
    }

    public List<Account> findAll() throws SQLException {
        String sql = "select * from t_account ";
        return runner.query(sql , new BeanListHandler<Account>( Account.class));
    }
}

package com.itheima.service;

import com.itheima.bean.Account;

import java.util.List;

public interface AccountService {
    int add(Account account) throws Exception;
    int delete(int id) throws Exception;
    int update(Account account) throws Exception;
    Account findById(int id) throws Exception;
    List<Account> findAll() throws Exception;
}

package com.itheima.service.impl;

import com.itheima.bean.Account;
import com.itheima.dao.AccountDao;
import com.itheima.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.List;

/*
    1. 把这个类交给spring管理
        打注解:  @Service
    2. 让spring把dao的对象给注入进来

        @Autowired
        private AccountDao dao;
 */
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao dao;

    public int add(Account account) throws SQLException {
        return dao.add(account);
    }

    public int delete(int id) throws SQLException {
        return dao.delete(id);
    }

    public int update(Account account) throws SQLException {
        return dao.update(account);
    }

    public Account findById(int id) throws SQLException {
        return dao.findById(id);
    }

    public List<Account> findAll() throws SQLException {
        return dao.findAll();
    }
}

driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/day41_spring
user=root
password=root

package com.itheima.config;

/*
    这是一个核心配置类
 */

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;


@Configuration //这是核心配置类
@ComponentScan("com.itheima") //扫描组件
@PropertySource("db.properties")
public class AppConfig {

    @Value("${driverClass}")
    private String driverClass;

    @Value("${jdbcUrl}")
    private String jdbcUrl;

    @Value("${user}")
    private String user;

    @Value("${password}")
    private String password;


    //把QueryRunner交给spring管理
    @Bean
    public QueryRunner runner(DataSource ds){
        return new QueryRunner(ds);
    }

    //把DataSource交给spring管理
    @Bean
    public DataSource ds () throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
}

功能测试

package com.itheima.test;

import com.itheima.bean.Account;
import com.itheima.config.AppConfig;
import com.itheima.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.sql.SQLException;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestAccountServiceImpl {

    @Autowired
    private AccountService as;

    @Test
    public void testAdd() throws SQLException {
        Account a = new Account();
        a.setName("zhagnsan");
        a.setMoney(10);
        as.add(a);
    }

    @Test
    public void testDelete() throws SQLException {
        as.delete(6);
    }
    @Test
    public void testUpdate() throws SQLException {
        Account a = as.findById(3);
        a.setMoney(999);

        as.update(a);
    }

    @Test
    public void testFindAll() throws SQLException {
        System.out.println(as.findAll());
    }
}

五、注解深入【拓展了解】

准备环境

1. 创建Module,引入依赖

 <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <!-- Spring整合Junit -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <!-- Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- snakeyaml,用于解析yaml文件的工具包 -->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.25</version>
        </dependency>

        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- c3p0连接池 -->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
    </dependencies>

2. 创建核心配置类

@Configuration
@ComponentScan("com.itheima")
public class AppConfig {
    
}

3.创建Service

package com.itheima.service;

public interface UserService {
    void add();
}
package com.itheima.service.impl;

import com.itheima.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("调用了UserServiceImpl的add方法~!~!");
    }
}

4.创建单元测试类

package com.itheima.test;

import com.itheima.config.AppConfig;
import com.itheima.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestUserServiceImpl {

    //把工厂给注入进来
    @Autowired
    private ApplicationContext context;


    @Test
    public void testBeanNames(){

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println("name = " + name);
        }

    }
}

@ComponentScan

这个注解的作用就是用来扫描指定包下的所有类。如果哪个类身上打了注解(@Controller | @Service | @Repository | @Component),就被spring给管理起来。默认情况下Spring管理这些对象的时候,他们的id名字就是类的名字,但是第一个字母小写。我们是否可用修改这种命名策略呢?

BeanName生成策略

说明
  1. 创建Java类,实现BeanNameGenerator接口,定义BeanName生成策略

  2. 在注解@ComponentScan中,使用nameGenerator属性指定生成策略即可

示例
  1. 创建Java类,实现BeanNameGenerator接口,定义BeanName生成策略
package com.itheima.demo1_componentscan;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;

/*
    自定义的id生成策略
        1. id的生成靠的是generateBeanName的返回值
        2. 这个方法的返回值是什么,id就是什么。
 */
public class MyBeanNameGenerator implements BeanNameGenerator {

    /**
     * 用来创建id值
     * @param beanDefinition 表示(包装)现在正在扫描(处理)的类
     * @param beanDefinitionRegistry
     * @return 对象的id值
     */
    public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) {

        //1 得到现在正在扫描到的这个类的全路径地址:
        String className = beanDefinition.getBeanClassName();

        System.out.println("className = " + className);

        //2. 返回的是全路径。
        return className;
    }
}

  1. 在注解@ComponentScan中,使用nameGenerator指定生成策略
@Configuration
@ComponentScan(value = "com.itheima" , nameGenerator = MyBeanNameGenerator.class) //扫描包 &  命名策略
public class AppConfig {
}
  1. 执行上面的单元测试

扫描规则过滤器

说明
示例1-根据注解过滤

哪个类身上有指定的注解,那么就忽略它 , 不扫描。这是按照注解的名字来忽略的。

@Configuration // 这是核心配置类
@ComponentScan(value = "com.itheima" , nameGenerator = MyBeanNameGenerator.class,
    excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION , classes = Service.class)
public class AppConfig {
}
示例2-根据指定类过滤
@Configuration // 这是核心配置类
@ComponentScan(value = "com.itheima" , nameGenerator = MyBeanNameGenerator.class,
    excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE , classes = UserServiceImpl.class) // 按照类不扫描
public class AppConfig {

}
示例3-自定义过滤
  1. 编写过滤器,实现TypeFilter接口,重写match方法
package com.itheima.demo1_componentscan;

import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;

/*
    这是自定扫描(包含|排除)的规则
 */
public class MyTypeFilter  implements TypeFilter {

    /**
     * 用于设置什么样的规则就排除|包含 类
     * @param metadataReader
     * @param metadataReaderFactory
     * @return true: 即表示匹配规则, false: 即表示不匹配规则。
     * @throws IOException
     */
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        //1. 得到现在正要被检查的类的元数据
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        //2. 获取现在的类的全路径名字
        String className = classMetadata.getClassName();

        //如果哪个类的名字包含了User,那么就返回true
        return className.contains("User");
    }
}

  1. 使用注解@ComponentScan,配置过滤规则
@ComponentScan(value = "com.itheima" , nameGenerator = MyBeanNameGenerator.class,
    excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM , classes = MyTypeFilter.class) // 按照自定义的规则来忽略类不扫描!
)
public class AppConfig {
}
  1. 执行单元测试,看看打印的结果。

@PropertySource

yml配置文件介绍

jdbc:
	driver: com.mysql.jdbc.Driver # 注意:英文冒号后边必须有一个空格
	url: jdbc:mysql:///spring
	username: root
	password: root
	
jedis:
	host: localhost
	port: 6379

使用@PropertySource加载yml

说明
示例
  1. resources目录里创建yaml文件:application.yml
jdbc:
	driver: com.mysql.jdbc.Driver # 注意:英文冒号后边必须有一个空格
	url: jdbc:mysql:///spring
	username: root
	password: root
	
jedis:
	host: localhost
	port: 6379
  1. pom.xml增加导入依赖snakeyaml,它提供了解析yml文件的功能
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.25</version>
</dependency>
  1. 创建Java类,实现PropertySourceFactory接口,重写createPropertySource方法
public class YamlSourceFactory implements PropertySourceFactory {
    /**
     * 解析yaml配置文件
     * @param name 名称
     * @param resource 配置文件EncodedResource对象 
     * @return PropertySource
     */
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        //1. 创建yaml解析的工厂
        YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
        //2. 设置要解析的资源内容
        factoryBean.setResources(resource.getResource());
        //3. 把资源文件解析成Properties对象
        Properties properties = factoryBean.getObject();
        //4. 把properties封装成PropertySource对象并返回
        return new PropertiesPropertySource("application", properties);
    }
}
  1. 使用@PropertySource注解,配置工厂类
@Configuration("appConfig")
@PropertySource(value = "application.yml" , factory = YamlSourceFactory.class) //导入yml文件
public class AppConfig {

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

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

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

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

    public void show(){
        System.out.println("driver = " + driver);
        System.out.println("url = " + url);
        System.out.println("username = " + username);
        System.out.println("password = " + password);
    }

}
  1. 测试
package com.itheima.test;

import com.itheima.config.AppConfig;
import com.itheima.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestAppConfig {

    @Autowired
    private AppConfig config;

    @Test
    public void testShow(){
        config.show();
    }
}

@Import

注册bean的方式

如果要注解方式配置一个bean,可以如下方式:

ImportSelector导入器

示例1-直接导入注册bean

使用@import来到一个类

@Configuration
@ComponentScan("com.itheima")
@Import(Teacher.class) 
public class AppConfig {
}
示例2-使用ImportSelector注册bean
package com.itheima.demo3_import;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

//自定义导入选择器
public class MyImportSelector implements ImportSelector {

    /**
     * 用于控制到底导入那个类到spring的容器|工厂里面
     * @param annotationMetadata
     * @return 数组,数组里面包含类的全路径地址。
     */
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{Teacher.class.getName() , Student.class.getName()};
    }
}

@Configuration
@ComponentScan("com.itheima")
@Import(MyImportSelector.class)
public class AppConfig {
    ...
}

示例3-ImportSelector的高级使用
说明

image-20200713154934953

第一步:创建新Module:module_a
<!-- module_a的坐标 -->
<groupId>com.itheima</groupId>
    <artifactId>spring_module_a</artifactId>
    <version>1.0-SNAPSHOT</version>

image-20200713160011267

  1. 在包com.a.beans里创建类 DemoBeanA

    @Component
    public class DemoBeanA {
    }
    
  2. 创建核心配置类AConfig

@Configuration
@ComponentScan("com.a")
public class AConfig {
}
  1. 创建导入器:创建Java类,实现ImportSelector接口,重写selectImports方法
public class AImportSelector implements ImportSelector {
    /**
     * @param importingClassMetadata。被@Import注解标注的类上所有注解的信息
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{AConfig.class.getName()};
    }
}
  1. 定义注解@EnableModuleA
@Import(AImportSelector.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableModuleA {
}
第二步:引入module_a,启动模块a
  1. 在我们自己的工程pom.xml里增加依赖
<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>spring_module_a</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
  1. 在我们自己的工程核心配置类上,使用注解@EnableModuleA启动模块a
@Configuration
@EnableModuleA
public class AppConfig {

}
  1. 在我们自己的工程里测试
   @Test
    public void testBean(){
        //在我们自己的工程里,可以获取到module_a里的bean
        DemoBeanA demoBeanA = app.getBean(DemoBeanA.class);
        System.out.println(demoBeanA);
    }

ImportBeanDefinitionRegister注册器

说明
示例
  1. 创建类com.other.Other
public class Other {
    public void show(){
        System.out.println("Other.show....");
    }
}
  1. 创建注册器
public class CustomImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
    /**
     * @param importingClassMetadata 当前类的注解信息
     * @param registry 用于注册bean的注册器
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取bean定义信息
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition("com.other.Other").getBeanDefinition();
        //注册bean,方法参数:bean的id,bean的定义信息
        registry.registerBeanDefinition("other", beanDefinition);
    }
}
  1. 在核心配置类上,使用@Import配置注册器
@Configuration
@Import({CustomImportBeanDefinitionRegister.class})
public class AppConfig {

}
  1. 测试
@Test
public void testImportRegister(){
    //获取扫描范围外,使用ImportBeanDefinitionRegister注册的bean
    Other other = app.getBean("other",Other.class);
    other.showOther();
}

@Conditional (条件)

说明

示例

  1. 创建Person类
package com.itheima.demo4_conditional;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private int age;
}


  1. 创建两个Java类,实现Condition接口
package com.itheima.demo4_conditional;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/*
    判断当前的操作系统是不是windows
 */
public class WindowsCondition implements Condition {

    /*
        返回true:  即表明是windows操作系统
        返回false :  即表示不是windows系统
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

        //1. 得到当前的环境对象
        Environment environment = conditionContext.getEnvironment();

        //2. 得到当前系统的名字
        String osName = environment.getProperty("os.name");

        return osName.contains("Windows");
    }
}

package com.itheima.demo4_conditional;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/*
    判断当前的操作系统是不是Linux
 */
public class LinuxCondition implements Condition {

    /*
        返回true:  即表明是Linux操作系统
        返回false :  即表示不是Linux系统
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

        //1. 得到当前的环境对象
        Environment environment = conditionContext.getEnvironment();

        //2. 得到当前系统的名字
        String osName = environment.getProperty("os.name");

        return osName.contains("Linux");
    }
}

  1. 核心配置类
@Configuration
@ComponentScan("com.itheima")
public class AppConfig {
    
    ...

  
    //=========================以下代码使用@Conditional来判断创建对象============================

    @Bean
    @Conditional(WindowsCondition.class)
    public Person windows(){
        System.out.println("创建了windows对象!~");
        return new Person("比尔", 75);
    }

    @Bean
    @Conditional(LinuxCondition.class)
    public Person linux(){
        System.out.println("创建了linux对象!~");
        return new Person("林纳斯", 62);
    }


     ...

}
  1. 测试
package com.itheima.test;

import com.itheima.config.AppConfig;
import com.itheima.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestUserServiceImpl {

    //把工厂给注入进来
    @Autowired
    private ApplicationContext context;


    @Test
    public void testBeanNames(){

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println("name = " + name);
        }

    }
}


执行一次单元测试方法之后,按照以下方式,可以通过JVM参数的方式,设置os.name的参数值。

设置之后,再次执行单元测试方法

image-20200713161316994

image-20200713161358617

Conditional的扩展注解

@Conditional在springboot里应用非常多,以下列出了一些@Conditional的扩展注解:

@Profile

说明
示例
  1. pom.xml中增加导入依赖mysql驱动, c3p0
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
    </dependency>
    
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>
  1. 在配置类里创建三个数据源,并配置@Profile
@Configuration
public class AppConfig {

     //生成三个数据源
    @Bean
    @Profile("dev")
    public DataSource devDataSource() throws PropertyVetoException {
        System.out.println("dev  开发环境的");
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql:///devdb");
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setMaxPoolSize(20);
        return dataSource;
    }

    @Bean
    @Profile("test")
    public DataSource testDataSource() throws PropertyVetoException {
        System.out.println("test  测试环境的");
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql:///testdb");
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setMaxPoolSize(20);
        return dataSource;
    }


    @Bean
    @Profile("release")
    public DataSource releaseDataSource() throws PropertyVetoException {
        System.out.println("release  生产环境的");
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql:///releasedb");
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setMaxPoolSize(20);
        return dataSource;
    }
}
  1. 在测试类上,使用@ActiveProfiles激活哪个环境,哪个环境的数据源会生效

    或者使用JVM参数-Dspring.profiles.active=dev

package com.itheima.test;

import com.itheima.config.AppConfig;
import com.itheima.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ActiveProfiles("dev")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestUserServiceImpl {

    //把工厂给注入进来
    @Autowired
    private ApplicationContext context;


    @Test
    public void testBeanNames(){

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println("name = " + name);
        }

    }
}

六、动态代理复习

1. JDK的基于接口的动态代理

image-20200406185202784

API介绍

Proxy
InvocationHandler接口

使用示例

有目标类(待增强的类)
 <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>


        <!-- Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
package com.itheima.proxy.jdk;

public interface Star {

    void sing(String name);
    void dance(String name);
}

package com.itheima.proxy.jdk;

public class SuperStar implements Star {
    public void sing(String name) {
        System.out.println("明星在唱歌:" + name);
    }

    public void dance(String name) {
        System.out.println("明星在跳舞:" + name);
    }
}
有通知类(用于进行功能增强的)

主要是用来做增强的。

package com.itheima.proxy.cglib;

//增强出来的功能
public class StarAdvice {

    public void before(){
        System.out.println("先彩排一下~~");
    }

    public void after(){
        System.out.println("收钱~~");
    }
}


使用动态代理生成代理对象
package com.itheima.test;

import com.itheima.proxy.jdk.Star;
import com.itheima.proxy.jdk.StarAdvice;
import com.itheima.proxy.jdk.SuperStar;
import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestJDKProxyDemo {

    @Test
    public void test01(){

        //1. 创建真实对象
        final Star realObj = new SuperStar();

        //2. 创建代理
        Star proxyObj = (Star) Proxy.newProxyInstance(
                realObj.getClass().getClassLoader(), //类加载器
                realObj.getClass().getInterfaces(), //实现的接口
                new InvocationHandler() { //调用处理器
                    /*
                        这个方法是代理对象和真实对象对话的桥梁。
                            1. 在外面用代理对象做任何事情,这个invoke方法都会被调用!
                            2. 在这个方法里面就得让真实对象去唱歌|跳舞!
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //System.out.println("invoke~!~");
                        //正向调用:  对象.方法(参数);
                        //realObj.sing("男人哭吧哭吧!");

                        //增强:
                        StarAdvice sa = new StarAdvice();
                        sa.before();

                        //反射调用: 方法对象.invoke(对象 , 参数);
                        Object result = method.invoke(realObj , args);

                        sa.after();

                        return result;
                    }
                });

        //3. 让代理唱歌:
        proxyObj.sing("喵喵喵...");
        proxyObj.dance("广场舞...");

    }
}

2. cglib的基于子类的动态代理【了解】

image-20200406191339226

API介绍

Enhancer
MethodInterceptor接口

使用示例

目标类(待增强的)
package com.itheima.proxy.cglib;

public class SuperStar {
    public void sing(String name) {
        System.out.println("明星在唱歌:" + name);
    }

    public void dance(String name) {
        System.out.println("明星在跳舞:" + name);
    }
}

通知类(用于增强的)
package com.itheima.proxy.cglib;

//增强出来的功能
public class StarAdvice {

    public void before(){
        System.out.println("先彩排一下~~");
    }

    public void after(){
        System.out.println("收钱~~");
    }
}

使用cglib生成代理对象
package com.itheima.test;

import com.itheima.proxy.cglib.StarAdvice;
import com.itheima.proxy.cglib.SuperStar;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class TestCglibProxyDemo {

    @Test
    public void test01(){

        //1. 创建真实对象
        final SuperStar realObj = new SuperStar();

        //2. 创建代理
        SuperStar proxyObj = (SuperStar) Enhancer.create(realObj.getClass(), new MethodInterceptor() {
            /*
                o : 代理对象
                method: 方法对象
                objects : 方法参数
                methodProxy : 方法代理
             */

            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

                //增强:
                StarAdvice sa = new StarAdvice();
                sa.before();

                //System.out.println("intercept...");
                //调用真实对象的方法
                //Object result = method.invoke(realObj ,objects);

                Object result = methodProxy.invokeSuper(o , objects);

                sa.after();

                return result;
            }
        });

        //3. 调用方法
        proxyObj.sing("世界第一等");
        proxyObj.dance("广场舞");

    }
}

总结

标签:Spring02,springframework,day42,bean,org,import,com,public
来源: https://www.cnblogs.com/ofanimon/p/16182689.html