• spring(一)


    Spring的简述

    1. Spring 是一个轻量级的框架(依赖少,消耗资源少)

    2. Spring是分层的架构,也就是y一个分层的JavaEE Full Stack(一站式)的轻量级开源框架

      也就是在经典三层(web , service , dao 层都有相应的技术解决方案)

    3. web : springMVC

      service:spring (可以做事务管理,切面编程AOP)

      dao:hibernate,mybatis,jdbcTemplate , spring-data

    Spring的优点

    1. 方便解耦,简化开发

      (高内聚,低耦合)

      比如分层的时候,dao层的所有技术都放在一起,就是高内聚

      低耦合:不同的技术之间,能不调用就不调用 (比如Spring的IOC)

      让不同的层之间形成低耦合

    Spring的模块

    Spring的Maven依赖导入

    注意:beans里面依赖包含了core,core包含了commons-logging

    IOC

    Inverse Of Control 控制反转

    简单来说:以前是自己New一个对象实例出来,现在是由Spring容器提供一个对象实例给自己用。

    配置文件:

    Spring配置文件是有xml约束模板的,去创建或者复制即可

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    
        <!--配置Service-->
        <bean id="userService" class="com.jzp.a_ioc.UserServiceImpl"></bean>
    </beans>
    

    测试IOC

        @Test
        public void demo01(){
            //这是之前的开发
            UserServiceImpl userService = new UserServiceImpl();
            userService.addUser();
        }
    
        @Test
        public void demo02(){
            //spring IOC提供实例
            //1. 加载Spring配置的xml文件,获取Spring容器
            String xml = "applicationContext.xml";
            ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(xml);
            UserService userService = (UserService) ac.getBean("userService");
            userService.addUser();
        }
    

    问题:

    Spring内部是通过什么原理实现的IOC。在xml文件中配置了全限定类名,然后在工厂里根据类名,通过反射生产出实例。

    DI

    Dependency Inject 依赖注入

    什么是依赖,什么是注入 就是如

    public class B{
        private A a; // 称B依赖于A
        public void setA(A a){
            B.a = a; //通过setter方法进行注入,称为注入
        }
    }
    

    之前的开发:

    public class BookServiceImpl{
        //以前的开发   是Service与Dao耦合, 只要Dao的实现类一旦发生改变,那么这里也要改变,重新修改new的					实例名称
        //解决办法:自定义工厂,通过工厂提供
        private BookDao bookDao = new BookDaoImpl();
    	
        //spring的DI解决之后(Service实现类,使用Dao接口,不知道具体的方法)
        
    }
    

    模拟Spring DI的执行过程

    BookService bookService = new BookServiceImpl(); --->IoC
    
    BookDao bookDao = new BookDaoImpl(); --->IoC
    
    bookService.setBookDao(bookDao); 	--->DI
    
    

    标签是完成IoC

    标签是完成对象的属性注入DI

    BeanFactory和ApplicationContext的区别

    后者是前者的子接口

    后者功能更完善,且不会延迟加载(也就是第一次调用getBean()时才会实例化这个Bean),

    而后者是当Context(配置文件)加载完成的时候,就会立即实例化所有的Bean

    测试结果证明BeanFactory的延迟加载

    装配Bean基于XML

    Bean实例化方式

    1. 默认构造

    2. 静态工厂 :

      Spring本身就有工厂,为什么还要静态工厂呢?

      答:静态工厂实例化Bean是用来整合其他框架常用的技术

      这就说到了单例和多例,就说到了Servlet是单例的,Structs2是多例的

      Servlet为什么是单例的: https://blog.csdn.net/wgyscsf/article/details/50129367?ref=myread

    注意:在例子中,我们只是模拟了一下静态工厂是如何把实例交给Spring去管理的。

    但是实际开发中,我们的工厂注入实例给Spring的应用场景是:

    我们通过调用别人的工厂(在class中写需要的工厂的全限定类名+生产的实例方法) ,去注入给Spring

    普通工厂

    就是不用静态方法,通过生产工厂Bean,再通过工厂Bean里的工厂方法实例化对象

    public class MyBeanFactory {
    
        //通过简单工厂(非静态工厂)创建实例
    
        //也就是可以创建多个工厂实例
        public  UserService createService(){
            return new UserServiceImpl();
        }
    }
    
        <!--创建工厂Bean-->
        <bean id="MyBeanFactory" class="com.jzp.c_inject.c_factory.MyBeanFactory"></bean>
    
        <!--通过上面注册的工厂Bean,获取实例-->
        <bean id="userService" factory-bean="MyBeanFactory" factory-method="createService"></bean>
    

    Bean的种类

    1. 普通Bean

    2. FactoryBean : 这是一个接口,是一个特殊的Bean,具有工厂生产对象的能力,只能生产特定的对象

      有很多实现类,此接口提供getObject()方法返回对象实例

      API文档

    比如ProxyFactoryBean: 用于生产代理对象的Bean

    FactoryBean和BeanFactory的区别?

    BeanFactory: 是一个工厂,用来生产任意Bean

    而FactoryBean:是一个特殊的Bean, 可以生产出特定的Bean出来

    方法为:

    Bean的作用域

            UserService userService = ac.getBean("userService", UserService.class);
            UserService userService2 = ac.getBean("userService", UserService.class);
    
        <!--创建实例-->
        <bean id="userService" class="com.jzp.d_scope.UserServiceImpl" scope="prototype"></bean>
    

    Bean生命周期:

    Bean的初始化与销毁
    public void demo01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        String xmlPath = "applicationContext_f_lifecycle.xml";
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
        UserService userService = ac.getBean("userService", UserService.class);
    
        //测试销毁方法
        //通过反射获取到其子类或者父类的close()方法,并使用
        ac.getClass().getMethod("close").invoke(ac);
    }
    
    <bean id="userService" class="com.jzp.e_lifecycle.UserServiceImpl" init-method="myInit" destroy-method="myDestroy"></bean>
    
    后处理Bean

    也就是BeanPostProcessor接口,只要实现了这个接口,并注册这个Bean给Spring容器

        <!--注册后处理Bean-->
        <bean class="com.jzp.e_lifecycle.MyBeanPostProcessor"></bean>
    

    后处理Bean的编写

    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        //导入源码后,才会人性化一点
    
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("前方法");
            return bean;
        }
    
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("后方法");
            return bean;
        }
    }
    

    结果:

    后处理Bean的原理:

    A a = new A();

    a = B.before(a); //用钩子B取出a ,做初始化之前的处理方法,并返回一个新的Bean对象(也可以是原来的)

    a.init();

    a = B.after(a); //用钩子取出a,做初始化之后的处理方法,并返回一个新的或原来的Bean

    在这里返回a的JDK代理对象

    为什么在后处理Bean的after()方法里返回动态代理对象呢,而不是在init()里呢?

    答案:因为JDK代理对象是根据接口进行反射的,如果在init()里返回JDK代理对象,因为JDK代理对象

    内部是只有业务接口方法的(而没有在UserServiceImpl实现类里的init()这些方法),所以a.init() [此处a为JDK代理对象] 永远无法执行

    正确做法:在after()方法里再返回JDK代理对象

    //在这里开启事务

    a.addUser(); //一般我们会在这个业务方法执行之前,可以加事务控制,所以可以在此用JDK代理对象开启事务,

    ​ 关闭事务

    目的: 能够对这个Bean的方法进行动态代理,在前后做相应的如事务处理,或者性能监控

    //在这里关闭事务

    a.destroy()

    代理类源代码:

        public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
            System.out.println("后方法"+beanName);
    
            //返回JDK代理对象
            //目的: 能够对这个Bean的方法进行动态代理,在前后做相应的如事务处理,或者性能监控
    
            return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if(method.getName().equals("addUser")){
                        System.out.println("开启事务-------");
                        //生成代理对象
                        Object object = method.invoke(bean, args);
                        System.out.println("关闭事务-------");
                        //返回代理对象
                        return object;
                    }
                    return null;
                }
            });
        }
    

    问题:

    getClassLoader(),顺便复习JMM内存管理, 和动态代理的具体参数的含义和内部原理

    后处理Bean如何只作用于一个:

    在后处理Bean实现类里的方法参数中的beanName,然后判断beanName.equals("我们想要处理的Bean名字"),即可

    否则后处理Bean里的方法对所有Bean有效

    动态代理的只作用于指定的method,也是同理的实现,method.getName().equals("addUser")

    记住:后处理Bean必须是单例的

    属性依赖注入

    分为 手动装配 和 自动装配

    手动装配:一般是根据XML配置文件,或者注解

    1. 基于XML装配,构造方法,setter方法
    2. 基于注解装配

    自动装配: 就是根据一定的约束自动装配

    1. byType: 按类型装配
    2. byName:按名称装配
    3. constructor 构造装配
    4. autodetect: 不确定装配

    构造函数注入

    public class User {
        private Integer uid;
        private String name;
        private Integer age;
    
        public User(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public User(Integer uid, String name) {
            this.uid = uid;
            this.name = name;
        }
    
    <!--编译器报错:没有默认的构造函数可以使用,因为我在User里写了带参构造函数-->
    <!--所以要实验:构造函数的注入-->
    <bean id="user" class="com.jzp.f_xml.a.constructor.User">
        <constructor-arg name="name" value="zhanp"></constructor-arg>
        <constructor-arg name="age" value="20"></constructor-arg>
    </bean>
    

    第二种:

        <bean id="user" class="com.jzp.f_xml.a.constructor.User">
            <!--index为索引,type为当多个构造方法匹配时,如何区分注入的属性类型-->
            <constructor-arg index="0" type="java.lang.String" value="zhanp"></constructor-arg>
            <constructor-arg index="1" type="java.lang.Integer" value="20"></constructor-arg>
        </bean>
    

    装配Bean 基于注解

    ​ 注解:就是一个类,使用@注解名称
    ​ 开发中:使用注解 取代 xml配置文件。

    1. @Component取代
      @Component("id") 取代
      2.web开发,提供3个@Component注解衍生注解(功能一样)取代
      @Repository :dao层
      @Service:service层
      @Controller:web层
      3.依赖注入 ,给私有字段设置,也可以给setter方法设置
      普通值:@Value("")
      引用值:
      ​ 方式1:按照【类型】注入
      ​ @Autowired
      ​ 方式2:按照【名称】注入1
      ​ @Autowired
      ​ @Qualifier("名称")
      ​ 方式3:按照【名称】注入2
      ​ @Resource("名称")
      4.生命周期
      初始化:@PostConstruct
      销毁:@PreDestroy
      5.作用域
      @Scope("prototype") 多例

      注解使用前提,添加命名空间,让spring扫描含有注解类

    <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">
    	<!-- 组件扫描,扫描含有注解的类 -->
    	<context:component-scan base-package="com.itheima.g_annotation.a_ioc"> </context:component-scan>
    </beans>
    
  • 相关阅读:
    Oracle创建表空间、创建用户以及授权、查看权限
    zf2 数据库连接
    ZF2.0用户向导 —— 6. 数据库及模型(Models)
    zf2配置
    zend framework2使用教程【一】安装
    config/application.config.php
    zf2\config\application.config.php
    zf2 数据库album demo
    albumController.php
    登录
  • 原文地址:https://www.cnblogs.com/zhanp/p/10931931.html
Copyright © 2020-2023  润新知