• Spring 实践 -IoC


    Spring 实践

    标签: Java与设计模式


    Spring简单介绍

    Spring是分层的JavaSE/EE Full-Stack轻量级开源框架.以IoC(Inverse of Control 控制反转)和AOP(Aspect Oriented Programming 面向切面编程)为内核, 代替EJB的臃肿/低效/脱离现实.
    主页http://spring.io/
    此处输入图片的描写叙述

    IoC与DI

    • IOC: 即控制反转, 解决程序对象紧密耦合问题(方式: 工厂+反射+配置文件), 将程序中原来构造对象的操作,交给IoC容器, 当程序真正须要对象时,再找IoC容器获取.
    • DI: 即依赖注入, IoC容器须要为程序提供依赖对象,而所依赖的对象又依赖于其它对象,因此能够一次获取该对象所依赖的全部对象(如Controller依赖于Service, Service依赖于DAO, 因此Controller找Ioc容器获取Service, 当IoC容器提供Service的同一时候,DAO也同一时候注入到Service中)

      具体可參考: IoC框架(依赖注入 DI)

    Spring

    • 方便解耦,简化开发
      Spring就是一个大工厂,可将全部对象创建依赖关系的维护交给Spring管理;
    • AOP支持
      Spring支持面向切面编程,能够方便的实现对程序进行权限拦截/运行监控/缓存实现等功能;
    • 声明式事务管理
      仅仅需通过配置就可完毕对事务的管理,而无需手动编程;
    • 方便程序的測试
      Spring提供对Junit4支持,通过注解方便測试Spring程序;
    • 集成各种优秀框架
      Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:MyBatis/iBatis/Hibernate等)的直接支持;
    • 减少JavaEE API的使用难度
      Spring对JavaEE开发的一些API(如JDBC/JavaMail/远程调用等)提供了封装,大大减少API使用难度;

    初识Spring

    需求- 模拟用户注冊过程:

    • Spring依赖
      进行Spring的IoC/DI开发,仅仅须要导入Spring最核心依赖:core/beans/context/expression,为了看到DEBUG信息,我们还能够加上commons-logging, 而junit, 则是做单元測试必备的:
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>4.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
    </dependencies>
    • Controller
    /**
     * Created by jifang on 15/12/5.
     */
    public class UserController {
    
        /**
         * 依赖注入(DI): 在Spring构造UserController对象时, 能够同一时候将构造好的UserService对象注入(下同)
         */
        private IUserService userService;
    
        public IUserService getUserService() {
            return userService;
        }
    
        public void setUserService(IUserService userService) {
            this.userService = userService;
        }
    
        public void register(String userName, String password) {
            System.out.println("用户: " + userName + " 进行注冊...");
            userService.register(userName, password);
        }
    }
    • Service
    public interface IUserService {
        void register(String userName, String password);
    }
    public class UserServiceImpl implements IUserService {
    
        private IUserDao userDao;
    
        public IUserDao getUserDao() {
            return userDao;
        }
    
        public void setUserDao(IUserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void register(String userName, String password) {
            System.out.println("用户: " + userName + " 进行注冊...");
            userDao.add(userName, passProcess(password));
        }
    
        // 对密码进行加密处理
        private String passProcess(String password) {
            System.out.println("密码: " + password + "加密处理...");
            return password;
        }
    }
    • DAO
    public interface IUserDao {
        void add(String userName, String password);
    }
    public class UserDaoImpl implements IUserDao {
    
        @Override
        public void add(String userName, String password) {
            System.out.println("用户: " + userName + ", 密码: " + password + " 加入数据库");
        }
    }
    • 配置Bean
    <?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"> <bean id="userDao" class="com.fq.first.dao.impl.UserDaoImpl"> </bean> <bean id="userService" class="com.fq.first.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userController" class="com.fq.first.controller.UserController"> <property name="userService" ref="userService"></property> </bean> </beans>

    • 測试
    /**
     * Created by jifang on 15/12/5.
     */
    public class UserControllerTest extends TestCase {
    
        /**
         * 载入Spring容器
         */
        private ApplicationContext context;
    
        @Before
        public void setUp() throws Exception {
            context = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        }
    
        @Test
        public void testRegister() throws Exception {
            UserController controller = context.getBean("userController", UserController.class);
            controller.register("翡青", "123");
        }
    }
    1. 在程序中通过ApplicationContext接口载入Spring容器, 获取Spring工厂对象
      • ClassPathXmlApplicationContext //读取src下配置文件
      • FileSystemXmlApplicationContext //读取WEB-INF下配置文件
    2. Spring对象工厂- BeanFactory与ApplicationContext:
      • ApplicationContextBeanFactory的子接口,BeanFactory是Spring最核心工厂接口。

      • ApplicationContext提供很多其它功能(如国际化处理/自己主动装配Bean/不同应用层的Context实现)
      • ApplicationContext会在容器初始化时对当中管理Bean对象进行创建,BeanFactory会在对象获取时才进行初始化.

    XML装配

    Spring提供了两种装配Bean的方式, XML与注解,当中XML方式Spring支持较早,如今在配置一些不是自己写的Bean时(如数据库连接池等从Jar包种引入的Bean)时是很实用,而注解方式则经常使用于装配自己写的Bean.


    三种实例化Bean的方式

    • 构造器实例化
    <!--使用构造器(默认无參)构造对象-->
    <bean id="constructBean" class="com.fq.instance.ConstructBean">
    </bean>
    • 静态工厂的静态方法实例化
    <!--使用静态工厂构造对象, 注: class应为工厂类-->
    <bean id="staticBean" class="com.fq.instance.StaticBeanFactory" factory-method="getInstance">
    </bean>
    • 实例工厂的实例方法实例化
    <!--使用实例工厂构造对象, 注: 要先实例化工厂-->
    <bean id="beanFactory" class="com.fq.instance.InstanceBeanFactory">
    </bean>
    <!-- 再通过工厂对象的实例方法。构造目标对象 -->
    <bean id="instanceBean" factory-bean="beanFactory" factory-method="getInstance">
    </bean>

    Bean作用域

    类别 说明
    singleton 在容器中仅存在一个实例(单例模式)
    prototype 每次从容器中获取都返回一个新的实例,即每次调用getBean()时,都运行一次构造方法(lazy,原型模式)
    request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境(不经常使用)
    session 同一个Session共享一个Bean,仅适用于WebApplicationContext环境(不经常使用)
    globalSession 一般用于Porlet应用环境,该作用域仅适用于WebApplicationContext环境(不经常使用)
    • scope
    <!--Spring使用scope标签来制定bean的作用域(默觉得Singleton)-->
    <bean id="singletonBean" class="com.fq.instance.SingletonBean" scope="singleton">
    </bean>
    <bean id="prototypeBean" class="com.fq.instance.PrototypeBean" scope="prototype">
    </bean>

    Bean生命周期

    Spring初始化/销毁bean时, 有时须要作一些处理工作, 因此Spring能够在创建和销毁bean的时候调用bean的两个生命周期方法;

    /**
     * Created by jifang on 15/12/6.
     */
    public class LifecycleBean {
    
        public LifecycleBean() {
            System.out.println("Constructor ...");
        }
    
        /**
         * 声明周期方法需: 无參, 无返回值, 非static
         */
        public void setUp() {
            System.out.println("SetUp ...");
        }
    
        /**
         * 同上
         */
        public void tearDown() {
            System.out.println("TearDown ...");
        }
    }
    • 配置:
    <!-- init-method属性配置初始化方法。destroy-method属性配置销毁方法-->
    <bean id="lifecycleBean" class="com.fq.bean.LifecycleBean" init-method="setUp" destroy-method="tearDown">
    </bean>
    • 測试
    /**
     * Created by jifang on 15/12/6.
     */
    public class LifecycleBeanTest extends TestCase {
        private ClassPathXmlApplicationContext context;
    
        @Before
        public void setUp() throws Exception {
            context = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        }
    
        @Test
        public void testLifecycle(){
            LifecycleBean bean = context.getBean("lifecycleBean", LifecycleBean.class);
            System.out.println(bean);
        }
    
        @After
        public void tearDown() throws Exception {
            // 必须手动调用context的close方法, 才会运行bean的销毁方法
            context.close();
        }
    }

    初始化方法与构造方法的差别?
    1) 构造方法为对象申请空间, 完毕对象基本属性的初始化;
    2) 初始化方法主要完毕对象复杂构造过程;
    3) Java建议将对象复杂构造过程单独抽取出初始化方法, 如javax.servlet.GenericServlet
    init方法

    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    后处理器

    Spring提供了BeanPostProcessor接口,在构造Bean对象运行对象初始化(init-method)方法时能够对Bean进行处理;

    /**
     * Created by jifang on 15/12/6.
     */
    public class PrintBeanProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            // 能够依据beanName来决定对那个Bean进行后处理操作
            if (beanName.equals("lifecycleBean")) {
                System.out.println("后处理bean -- process before ...");
            }
    
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            // 假设不制定beanName, 则默认处理全部Bean
            System.out.println("后处理bean -- process after ...");
            return bean;
        }
    }
    • 配置
    <!-- 为Spring容器所用的bean, 不需配置id -->
    <bean class="com.fq.processor.PrintBeanProcessor"></bean>

    这样在运行init-method[setUp]的前后, 会分别运行BeanPostProcessor中的两个方法.

    后处理器能够在对象构造过程中提供代理,这是AOP自己主动代理的核心.


    XML依赖注入

    Spring配置文件支持构造參数属性注入和Setter方法属性注入;

    1. 构造參数注入

    <bean id="bean" class="com.fq.di.Bean">
        <!--
            index   代表參数顺序(从0開始)
            name    代表參数名
            type    參数类型
            value   注入的參数值
            ref     引用还有一个bean元素的id
         -->
        <constructor-arg index="0" type="java.lang.String" value="fei_qing"></constructor-arg>
        <constructor-arg index="1" type="java.lang.Double" value="3.14"></constructor-arg>
    </bean>

    2. Setter方法注入

    <bean id="bean" class="com.fq.di.Bean">
        <!--
            name    属性名(congSetter方法获得)
            value   注入的參数值
            ref     引用的还有一个bean的id
         -->
        <property name="name" value="fei_qing"></property>
        <property name="price" value="88.8"></property>
    </bean>

    3. p名称空间注入

    P名称空间在spring2.5版本号后引入, 目的是为了简化属性依赖注入(setter方法)

    <!--
        p:属性名="XXX", 引入常量值
        p:属性名-ref="XXX", 引用其它Bean对象
    -->
    <bean id="bean" class="com.fq.di.Bean" p:name="feiqing" p:price="1188">
    </bean>

    4. SpEL表达式

    在spring3.0之后,引入SpEL表达式,以简化属性注入.

    #{表达式}, 通过value属性注入: 能够引用一个Bean对象/对象属性/对象方法… 具体可參考Spring 表达式语言(SpEL)

    • Bean
    public class Car {
        private String logo;
        private double price;
        private String owner;
    
        public String getLogo() {
            return logo;
        }
    
        public void setLogo(String logo) {
            this.logo = logo;
        }
    
        public double getPrice() {
            return price;
        }
    
        public void setPrice(double price) {
            this.price = price;
        }
    
        public String getOwner() {
            return owner;
        }
    
        public void setOwner(String owner) {
            this.owner = owner;
        }
    }
    public class Employ {
    
        private String name;
        private Car car;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Car getCar() {
            return car;
        }
    
        public void setCar(Car car) {
            this.car = car;
        }
    }
    • 配置
    <!--SpEL 使用#{}来引用/获取对象-->
    <bean id="car" class="com.fq.di.Car">
        <property name="logo" value="#{'logo.pic'}"/>
        <property name="price" value="#{18.8}"/>
        <property name="owner" value="#{'feiqing'}"/>
    </bean>
    
    <bean id="employ" class="com.fq.di.Employ">
        <!-- 能够直接使用value来引用到对象, 而不是ref -->
        <property name="car" value="#{car}"/>
        <!-- 能够直接引用一个对象的属性 -->
        <!--<property name="name" value="#{car.owner}"/>-->
        <!-- 还能够直接调用对象的方法 -->
        <property name="name" value="#{car.getOwner().toUpperCase()}"/>
    </bean>

    4. 集合属性注入

    java常见集合: List/Set/Map/Properties等, Spring为每种集合都提供一个标签进行注入;

    • Bean
    public class CollectionBean {
        private List<String> list;
        private Set<String> set;
        private Map<String, String> map;
        private Properties properties;
    
        public List<String> getList() {
            return list;
        }
    
        public void setList(List<String> list) {
            this.list = list;
        }
    
        public Set<String> getSet() {
            return set;
        }
    
        public void setSet(Set<String> set) {
            this.set = set;
        }
    
        public Map<String, String> getMap() {
            return map;
        }
    
        public void setMap(Map<String, String> map) {
            this.map = map;
        }
    
        public Properties getProperties() {
            return properties;
        }
    
        public void setProperties(Properties properties) {
            this.properties = properties;
        }
    }
    • 配置
    <bean id="collectionBean" class="com.fq.di.CollectionBean">
        <property name="list">
            <list>
                <value>aa</value>
                <value>bb</value>
                <value>cc</value>
                <value>dd</value>
            </list>
        </property>
    
        <property name="set">
            <set>
                <value>11</value>
                <value>12</value>
                <value>11</value>
            </set>
        </property>
    
        <property name="map">
            <map>
                <entry key="key1" value="value1"/>
                <entry key="key2" value="value2"/>
            </map>
        </property>
    
        <property name="properties">
            <props>
                <prop key="key1">value_1</prop>
                <prop key="key2">value_2</prop>
            </props>
        </property>
    </bean>

    注解装配

    注解配置Bean

    • 在须要Spring管理的类上加入@Component注解
      (@Component还能够指定组件名@Component(value = "xxx"))
    @Component
    public class Bean {
    
        private String name;
    
        private Double price;
    
        public Bean() {
        }
    
        public Bean(String name, Double price) {
            this.name = name;
            this.price = price;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Double getPrice() {
            return price;
        }
    
        public void setPrice(Double price) {
            this.price = price;
        }
    }
    • 引入context命名空间并批量扫描
    <?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"> <context:component-scan base-package="com.fq.di"/> </beans>

    Spring细化@Component以细分组件功能,提供了下面三个等价注解:

    注解 说明
    @Controller 控制器,web层组件
    @Service 业务类,业务层组件
    @Repository 持久层组件

    Bean作用域

    通过@Scope注解指定作用域

    @Component
    @Scope("prototype")
    public class Bean {
        // ...
    }

    Bean生命周期

    @PostConstruct 初始化
    @PreDestroy 销毁
    • Bean
    public class Bean {
    
        @PostConstruct
        public void setUp(){
            System.out.println("setUp ...");
        }
    
        @PreDestroy
        public void tearDown(){
            System.out.println("tearDown ...");
        }
    }

    注解依赖注入

    1. @Value

    • 简单类型
    @Component
    public class Bean {
    
        @Value("feiqing")
        private String name;
    
        @Value("88.88")
        private Double price;
    
        // ....
    }
    • 复杂属性(使用SpEL表达式)
    @Component
    public class Bean {
    
        @Value("#{car}")
        private Car car;
    
        // ...
    }

    2. @Autowired

    • @Autowired 默认依照类型进行注入(假设容器中存在两个同样类型对象,则@Autowired无法注入)
    @Component
    public class Bean {
    
        @Autowired
        private Car car;
    
        // ....
    }
    • @Autowired+@Qualifier指定注入Bean的id
    @Component
    public class Bean {
    
        @Autowired
        @Qualifier("car")
        private Car car;
    
        // ...
    }

    3. @Resource

    Spring支持JSR-250规范,能够使用@Resource()进行属性注入,功能和@Autowired同样:

    @Controller(value = "bean")
    public class Bean {
    
        @Resource(name = "car")
        private Car car;
    
        //...
    }

    注解/XML混合

    Bean定义使用XML,Bean关系依赖注入使用注解:

    须要在applicationContext.xml中配置:

    <context:annotation-config/>

    该配置能够使@Resource@PostConstruct@PreDestroy@Autowired注解生效.

    假设在配置文件里使用了<context:component-scan base-package="xxx.xx"/>则具有了<context:annotation-config/>的效果, 不必再单独配置.


  • 相关阅读:
    ElEmentUI选择器弹出框定位错乱问题解决(弹出框出现在左上角)
    Element中(Notification)通知组件字体修改(Vue项目中Element的Notification修改字体)
    解决谷歌浏览器设置font-family属性不起作用,(css中设置了font-family:没有用)css字体属性没用
    开发环境Vue访问后端接口教程(前后端分离开发,端口不同下跨域访问)
    nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.报错解决
    ssm项目中中文字符乱码
    idea使用PlantUML画类图教程
    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3报错解决
    安装anaconda python时只能安装到默认文件夹&& 安装提示文件夹以存在问题
    生产者消费者代码学习,Producer_Consuner
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/7401037.html
Copyright © 2020-2023  润新知