Spring 实践 -IoC

Spring 实践


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/>的效果, 不必再单独配置.


时间: 2024-11-01 11:56:15

Spring 实践 -IoC的相关文章

Spring核心技术IoC容器(六)

前文已经描述了Bean的作用域,本文将描述Bean的一些生命周期作用,配置还有Bean的继承. 定制Bean 生命周期回调 开发者通过实现Spring的InitializeingBean和DisposableBean接口,就可以让容器来管理Bean的生命周期.容器会调用afterPropertiesSet()前和destroy()后才会允许Bean在初始化和销毁Bean的时候执行一些操作. JSR-250的@PostConstruct和@PreDestroy注解就是现代Spring应用生命周期回

Spring的Ioc与DI

一.前言 Spring框架的核心基于控制反转的原理. IoC是一种将组件依赖关系的创建和管理外部化的技术. 考虑一个示例,其中Foo类依赖于Bar类的实例来执行某种处理. 传统上,Foo使用new运算符创建Bar的实例,或者从某种工厂类中获取一个实例. 使用IoC方法,运行时某些外部进程会将Bar的实例(或子类)提供给Foo. 这种行为,即在运行时注入依赖项,导致IoC被Martin Fowler重命名为更具描述性的依赖项注入(DI).依赖注入是IoC的一种特殊形式,尽管您经常会发现这两个术语可

spring中IOC的简单使用

spring的ioc主要就是依赖注入,有基于构造器的依赖注入还有通过设值注入,这里我只简单的实现设值注入的方法,通过spring的依赖管理,我们可以很方便的了解各层之间的依赖关系,降低了各层之间的耦合,我们可以不用过多的去关注对象的管理和创建,我们只需要去bean工厂申请即可,这样我们更多的注意力就可以放到真正的代码实现,而不用去关心代码的创建和销毁.,接下来时简单的设值注入. 我们先简单的去创建一个学生类和老师类 public class Student { public void outhi

Spring之IOC容器注入

上一篇做了IOC和AOP的原理介绍,这一篇主要讲解IOC的注入.不过我依然困惑一个问题: 一 : 依赖注入(DI)中有三种注入方式,那Spring中到底实现了几种方式?也是三种? IOC在很多框架中都有实现,并不是Spring特有的,之前说过IOC主要包含DL(Dependency Lookup)和DI(Dependency Injection),也就是说实现IOC的技术有很多,但是主要包含DI和DL,但是相对而言,DI应用范围比较广泛,我想这也是为什么Martin Fowler将控制反转用依赖

Spring的Ioc

引用:http://www.cnblogs.com/xdp-gacl/p/4249939.html 学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring Ioc的理解. 一.分享Iteye的开涛对Ioc的精彩讲解 首先要分享的是Iteye的开涛这位技术牛人对Spring框

一点一点学架构(三)——Spring.NET IOC

IOC背景介绍 传统的实现: 由程序内部代码来控制类与类之间的关系(如:在一个具体的类中,调用另一个类的方法). 使用new关键字来实现两个类之间关系的组合. 这种实现方式会造成类之间耦合. IOC的实现: 它将类间关系从程序内部提到外部容器,也就是说由容器在运行期将类间的某种依赖关系动态注入类中. 对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A. IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的

聊聊spring的ioc

平时我们老说的控制反转依赖注入是什么东西?其实说白了就是一个松紧耦合的问题,咱们可以想一想,一个javabean里面可能会有很多属性比如像下面的代码: public class KnightOfTheRoundTable implements Knight { private String name; private Quest quest; public KnightOfTheRoundTable(String name) { this.name = name; } public Object

spring的IOC和AOP详细讲解

1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是有这个IOC容器进行产生,同时,要是产生的是单例的bean,他还可以给管理bean的生命周期! spring的IOC有三种注入方式 : 第一是根据属性注入 也叫set方法注入: 第二种是根据构造方法进行注入: 第三种是根据注解进行注入,这种方式我认为比较好,方便,要是bean多的话,使用前两种方式会使得配置文件过于臃肿. Spri

Spring之IOC容器加载初始化的方式

引言 我们知道IOC容器时Spring的核心,可是如果我们要依赖IOC容器对我们的Bean进行管理,那么我们就需要告诉IOC容易他需要管理哪些Bean而且这些Bean有什么要求,这些工作就是通过通过配置文件告诉Spring 的IOC容器.在我们的完成这些配置文件以后,如果IOC容易要实现对这些Bean的管理,除了资源的定位还有一个很重要的一步就是完成IOC加载初始化,也就是配置文件的载入过程.完成IOC容器加载初始化的方式只要有三种,第一种就是通过File文件加载,第二种是通过Classpath