zoukankan      html  css  js  c++  java
  • 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实现 洛谷 P1464 Function
    java实现 洛谷 P1464 Function
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1540 机器
    java实现 洛谷 P1540 机器
  • 原文地址:https://www.cnblogs.com/lwj-0923/p/7446091.html
Copyright © 2011-2022 走看看