• Spring(二)--FactoryBean、bean的后置处理器、数据库连接池、引用外部文件、使用注解配置bean等


    实验1:配置通过静态工厂方法创建的bean  [通过静态方法提供实例对象,工厂类本身不需要实例化!]

    1.创建静态工厂类

    public class StaticFactory {
    
          private static HashMap<String, Book> map = null;
    
          static{
                map = new HashMap<>();
                map.put("book01", new Book("java", "you", 33.33));
                map.put("book02", new Book("c++", "you", 66.66));
                map.put("book03", new Book("c#", "you", 99.99));
          }
    
          public static Book getBookById(String id){
                return map.get(id);
          }
    }
    

     工厂本身不创建对象,而是通过其提供的静态方法获取对象

    <bean id="staticFactory" class="com.neuedu.spring.entity.StaticFactory" factory-method="getBookById">
          <constructor-arg value="book02"></constructor-arg>
    </bean>
    
    如果不写 <constructor-arg> 会报 method="getBookById" 构造器需要参数的错误
    所以通过<constructor-arg> 传一个参数
    在 junitTest 中会获取到 book02 中的内容

     实验2:配置通过实例工厂方法创建的bean  [通过实例方法提供实例对象,工厂类本身需要先创建对象!]

    实例工厂先创建一个工厂对象,然后通过工厂对象的方法获取一个实例
    将静态工厂类中的方法复制,并修改为非静态方法 【去除static】
    public class InstanceFactory {
    
          private  HashMap<String, Book> map = null;
    
          {
                map = new HashMap<>();
                map.put("book01", new Book("java", "you", 33.33));
                map.put("book02", new Book("c++", "you", 66.66));
                map.put("book03", new Book("c#", "you", 99.99));
          }
    
          public Book getBookById(String id){
    
                return map.get(id);
          }
    }
    
    先给工厂类本身创建一个对象<bean id="instanceFactory" class="com.neuedu.spring.entity.InstanceFactory"></bean>
    再给方法创建一个对象 ,bean中不用写 class ,写 factory-bean,依赖于 instanceFactory
    此时需要一个工厂方法factory-method
    还需要给工厂方法提供一个参数<constructor-arg value="book03">
    <bean id="instanceFactory" class="com.neuedu.spring.entity.InstanceFactory"></bean>
    <bean id="bookFromInstanceFactory" factory-bean="instanceFactory" factory-method="getBookById">
          <constructor-arg value="book03"></constructor-arg>
    </bean>
    

    从实例中获取book03

    public void test() {
        Object bean = ioc.getBean("bookFromInstanceFactory");
        System.out.println(bean);
    }
    

    实验3:配置FactoryBean

    创建 MyFactoryBean 实现 FactoryBean<>接口
    返回对象就是调用 getObject() 方法
    public class MyFactoryBean implements FactoryBean<Book>{
    
          @Override
          public Book getObject() throws Exception {
                //Spring的IOC容器就是调用该方法返回的对象
                return new Book("java", "you", 33.33);
          }
    
          @Override
          public Class<?> getObjectType() {
                //返回对象的类型
                return Book.class;
          }
    
          @Override
          public boolean isSingleton() {
                return true;
          }
    }
    
    <!-- 配置工厂bean -->
    <bean id="myFactoryBean" class="com.neuedu.spring.entity.MyFactoryBean"></bean>
    

    实验4:测试bean的后置处理器

    在bean的初始化方法【init】调用前后执行操作的专门的对象
    Object 代表 bean 对象,String 代表 bean 对象的 id
    自定义后置处理器实现该接口:BeanPostProcessor
     
    public class MyBeanPostProcessor implements BeanPostProcessor{
    
          @Override
          public Object postProcessAfterInitialization(Object object, String beanId) throws BeansException {
                System.out.println("After---"+object+"---"+beanId);
                return object;
          }
    
          @Override
          public Object postProcessBeforeInitialization(Object object, String beanId) throws BeansException {
                System.out.println("Before---"+object+"---"+beanId);
                return object;
          }
    }
    

     没有 bean 对象,后置处理器不会执行

    <bean id="myBeanPostProcessor" class="com.neuedu.spring.entity.MyBeanPostProcessor"></bean>
    

     如果只这么写的话,是不会输出什么的

     

    所以在这之前需要创建一个 bean 对象

    <bean id="book" class="com.neuedu.spring.entity.Book" init-method="init">
         <property name="bookName" value="java"></property>
         <property name="author" value="you"></property>
         <property name="price" value="32.32"></property>
    </bean>
    <bean id="myBeanPostProcessor" class="com.neuedu.spring.entity.MyBeanPostProcessor"></bean>
    
    在 Book 类中创建 init( ) 方法,输出“Book对象的初始化方法”
    以 观察是否在对象的初始化方法之前和之后处理

    bean对象属性的检查,属性名、属性类型是否规范


    数据库连接池:
     
    > 数据库连接池就是存放数据库连接(Connection)的集合
    > 我们获取一个数据库连接是一个相对很麻烦的过程,
       如果我们获取一个数据库连接,使用一次以后就给它关闭了
       下一次再去使用的时候就要重新创建一个新的数据库连接
    > 所以我们提出了一个数据库连接池的概念,数据库连接池放的都是数据库连接(Connection)
       我们在去使用数据库连接时候,不用再去重新创建数据库连接,而是直接从池中获取,
        使用完的数据库连接,也不是直接销毁,而是要放回到连接池。
     
    > 数据库连接池的常见的属性:
       初始连接数量:数据连接池创建以后,保存数据库连接的数量
     
       最小空闲连接数:数据库连接池最少得未使用的数据库连接的数量
     
       最大空闲连接数:数据库连接池最大闲置连接数,当闲置连接数满了以后,将不会有其他连接进入池
     
       每次增加连接数:当数据库连接都被占用以后,一次性增加的数据库连接的个数
     
       最大连接数:数据库连接池的最大容量,当最大连接数饱和了,则不再创建新的数据库连接
     
       最大等待时间:当数据库连接池饱和以后,等待获取数据库连接的时间
     
    > 常见的数据库连接池
          - 所有的数据库连接池都需要实现DataSource,当使用数据库连接池是,我们便不再需要使用DriverManger获取数据库连接而是使用DataSource。
          - Connection getConnection()
          - 从数据库连接池中获取数据库连接对象
     
    1.DBCP
          - DBCP是Apache出品的一款数据库连接
          - DBCP依赖于commons-pool
          - 使用DBCP需要导入两个jar包:
                 commons-dbcp-1.4.jar
                 commons-pool-1.5.5.jar
          - 当我们通过数据库连接池获取数据库连接以后,我们所获取到数据库连接已经不是我们熟悉的那个Connection
             数据库连接池对Connection对象进行了包装,它修改Connection的close()方法
             再去调用close()数据库连接将不会真的关闭,而是要放回到数据库连接池中,供其他线程使用。
          - 核心类:
                BasicDataSourceFactory
     
    2.C3P0(重点)
           - C3P0使用的是XML作为配置文件
           - 使用c3p0需要导入一个jar包:c3p0-0.9.1.2.jar
           - 导入c3p0的配置文件:
             1.配置文件的名字:c3p0-cofig.xml
             2.配置文件要求放到类路径下(src)
           - 核心类: ComboPooledDataSource
           - 注意:
                    DataSource就相当于池子,我们的数据库连接都是从DataSource中获取的,
                    如果程序中有多个DataSource的实例,那么我们说你还不如不用数据库连接池。

     实验5:引用外部属性文件

    新建文件 jdbc.properties
     
    properties 中都是键值对格式
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
          <property name="user" value="${jdbc.username}"></property>
          <property name="password" value="${jdbc.password}"></property>
          <property name="driverClass" value="${jdbc.driver}"></property>
          <property name="jdbcUrl" value="${jdbc.url}"></property>
    </bean>
    
    加载了配置文件的地址location="classpath:jdbc.properties" ,所以${ }能直接使用
    通过 bean 中的连接信息
    可以直接从连接池获取连接 bean. getConnection()
    @Test
    public void test() throws SQLException {
          DataSource bean = ioc.getBean(DataSource.class);
          System.out.println(bean.getConnection());
    }
    

     执行查询操作

    @Test
    public void test() throws SQLException {
          DataSource bean = ioc.getBean(DataSource.class);
          Connection conn = bean.getConnection();
          String sql = "select * from student";
          PreparedStatement ps = conn.prepareStatement(sql);
          ResultSet rs = ps.executeQuery();
          while(rs.next()){
               String name = rs.getString("name");
               String email = rs.getString("email");
               String school = rs.getString("school");
               String score = rs.getString("score");
               System.out.println(name+"--"+email+"--"+school+"--"+score);
          }
    }
    

    补充:

    用注解就不用get/set方法
    新建一个 AController 类 ,属性是外部文件中的值,在类上加 Controller注解并导包【aop】
    加个注解 Value,注解里用EL表达式获取外部文件每个值
    @Controller
    public class AController {
    
          @Value("${jdbc.username}")
          private String username;
          //${jdbc.username}将值赋给username
          @Value("${jdbc.password}")
          private String password;
    
          @Value("${jdbc.driver}")
          private String driver;
    
          @Value("${jdbc.url}")
          private String url;
    
          @Override
          public String toString() {
                return "AController [username=" + username + ", password=" + password + ", driver=" + driver + ", url=" + url
                            + "]";
          }
    }
    
    <context:component-scan base-package="com.neuedu"></context:component-scan>
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    @Test
    public void test01(){
          AController bean = ioc.getBean(AController.class);
          System.out.println(bean);
    }
    

     实验6:基于XML的属性装配

    (1)手动配置
     
    新建两个类,一个类中包含另一个类为属性,
    <!-- 手动装配 -->
    <bean id="userService" class="com.neuedu.spring.entity.UserService"></bean>
    <bean id="userAction" class="com.neuedu.spring.entity.UserAction">
          <property name="userService" ref="userService"></property>
    </bean>
    
    (2)自动装配
     
    ① byType :如果UserAction 类中的userService的属性类型 UserService可以在IOC容器中找到,成功
                        与userService名字无关,只与属性类型相关
    <bean id="userService" class="com.neuedu.spring.entity.UserService"></bean>
    <bean id="userAction" autowire="byType" class="com.neuedu.spring.entity.UserAction"></bean>
    
    ② byName:如果 UserAction 中有属性名为userService 就可以在IOC容器中找到
                        只看名字
    <bean id="userService" class="com.neuedu.spring.entity.UserService"></bean>
    <bean id="userAction" autowire="byName" class="com.neuedu.spring.entity.UserAction"></bean>
    

     实验7:使用注解配置bean

    (1)声明bean的注解
            @Component 将当前类声明为IOC容器中的一个普通的组件 ,
            @Controller 将当前类声明为IOC容器中的一个控制器组件 ,接受web前端请求
            @Service 将当前类声明为IOC容器中的业务逻辑层组件 ,
            @Repository 将当前类声明为IOC容器中的一个持久化层组件,
     
    (2)使用基于注解的bean的配置,需要额外导入一个jar包:spring-aop-4.0.0.RELEASE.jar
    (3)需要设置自动扫描的包
    <context:component-scan base-package="com.neuedu"></context:component-scan>
    
    三种写法:
    (1)直接写类名
    (2)类名首字母小写
    (3)在@Controller 后 加上重定义的名字 ,如@Controller(value="asd")
              value 可省
    @Test
    public void test(){
    //     UserController bean = ioc.getBean(UserController.class);
    //     Object bean = ioc.getBean("userController");
           Object bean = ioc.getBean("asd");
           System.out.println(bean);
    }
    

    Spring根据上述注解其实并不能分辨当前类是否真的是一个Controller或Dao,因为标记的类和注解不对应也没有语法错误。也就是将 controller层的注解写成@Service 也没错,但在实际工作中,肯定要将专门的注解标记在对应的类上面。


     实验8:通过注解分别创建Dao、Service、Controller

    分别在以下三层,依次调用下一层的方法,最后在dao层输出一句话
    在Test 中观察是否能输出那句话
    @Controller
    public class UserController {
          @Autowired
          private UserService userService;
    
          public void getStudentInfo(){
                userService.getStudentInfo();
          }
    }
    
    @Service
    public class UserService {
          @Autowired
          private UserDao userDao;
    
          public void getStudentInfo() {
                userDao.getStudentInfo();
          }
    }
    
    @Repository
    public class UserDao {
          public void getStudentInfo() {
                System.out.println("enenenenen");
    
          }
    }
    
    @Test
    public void test(){
          UserController bean = ioc.getBean(UserController.class);
          bean.getStudentInfo();
    }
    

     实验9:使用context:exclude-filter指定扫描包时不包含的类

                 使用context:include-filter指定扫描包时要包含的类
     
    expression 中写的是扫描包时不包含的类的全类名【注解的全类名】
    <context:component-scan base-package="com.neuedu">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
    base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类
     
    use-default-filters 默认为true ,当为false 时,代表不再使用默认的过滤器,在IOC容器中都找不到了
    但是include-filter 代表要扫描的包,所以在Test中可以在 IOC中找到controller
    <context:component-scan base-package="com.neuedu" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
    使用的是所建类的全类名,而不是 @Controller 的全类名
    这样在 IOC容器中也找不到
    <context:component-scan base-package="com.neuedu">
    <context:exclude-filter type="aspectj" expression="com.neuedu.spring.entity.UserController"/>
    </context:component-scan>
    

     实验10:使用@Autowired注解实现根据类型实现自动装配

    [1]首先检测标记了@Autowired注解的属性的类型
    [2]根据类型进行装配
    [3]如果指定类型的bean不止一个,那么根据需要被装配的属性的属性名做id的值,查找bean
    [4]如果根据id值还是没有找到bean,可以使用 @Qualifier 注解手动指定要装配的bean的id
     
    @Autowired 首先是根据类型来注入
    根据 service 的类型到 IOC中找
    @Controller
    public class UserController {
          @Autowired
          private UserService service;
    
          public void say(){
                service.getStudentInfo();
          }
    }
    
    因为 service 的类型是 UserService ,所以当 UserService有一个子类的时候
    根据类型就会找不到
    所以就会通过姓名查找
    通过更改 service层的value值
    @Service(value="service")
    public class UserService {
          @Autowired
          private UserDao userDao;
    
          public void getStudentInfo() {
                userDao.getStudentInfo();
          }
    }
    
     
    @Qualifier 指定一个value 值,去 IOC容器中找它
    @Autowired(required=false) 指:就算找不到也不会报错
    @Controller
    public class UserController {
          @Autowired(required=false)
          @Qualifier(value="BService")
          private UserService ser;
    
          public void say(){
                ser.getStudentInfo();
          }
    }
    
  • 相关阅读:
    java实现文件上传下载至ftp服务器
    理解java代理模式
    [置顶] 理解java中的线程池
    wait,notify,非阻塞队列实现生产者,消费者模型
    理解java阻塞队列BlockingQueue
    CentOS下安装配置Nginx
    putty笔记
    CentOs下安装jdk、MySql、Redis笔记
    简述yum和apt-get的区别
    Linux 文件颜色说明
  • 原文地址:https://www.cnblogs.com/lwj-0923/p/7446091.html
Copyright © 2020-2023  润新知