spring

spring

这里写图片描述

一、介绍

1、关于spring

spring是一个轻量级容器框架,用来管理web层、业务层、dao层,维护各个层之间的关系,将对象之间的依赖关系交由spring来控制,减少层与层之间的耦合,spring的核心功能是IOC和AOP。

2、缺点

spring本是一个轻量级的容器框架,主要包含IOC和AOP,用来管理各种bean,但是现在变得过于臃肿,包含有各种各样的东西,springMVC、spring Cloud等。还有就是spring的配置过于繁杂,感觉比敲代码还麻烦,需要记住各种配置。

3、引入

web项目要使用spring框架,需要注册监听器,在web.xml文件中配置:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext-*.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframe.web.context.ContextLoaderListener</listener-class>
</listener>

二、IOC

1、关于bean

1)bean

bean:bean是特殊的java类,可以理解为可复用的组件。

属性 描述
class 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。
name 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
scope 这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。
constructor-arg 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
properties 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
autowiring mode 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
lazy-initialization mode 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。
initialization 在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的生命周期章节中进行讨论。
destruction 当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章节中进行讨论。

scope:声明bean的作用域。

作用域 描述
singleton 在每个Spring IoC容器中一个bean定义对应一个对象实例。
prototype 一个bean定义对应多个对象实例。
request 在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例, 它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。
session 在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global-session 在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。

2)bean的生命周期

  • 初始化回调:实例化bean时,立即调用该方法。
<bean id="userService" class="com.service.UserService" init-method="init"/>
public class UserService{
    public void init(){

    }
}
  • 销毁回调:从容器中移除bean后,才能调用该方法。
<bean id="userService" class="com.service.UserService"  destroy-method="destroy"/>
public class UserService{
    public void destroy(){

    }
}

2、关于IOC

1)IOC

IOC控制反转:把创建对象(bean),和维护对象的关系的权利从程序中转移到spring的容器。一般的java程序我们如果需要使用某个类,我们需要定义该对象的引用,然后通过new的方法进行实例化,然后去使用该对象,但是有了spring的IOC后,我们只需定义对象的引用,然后由spring进行管理,实例化该对象供我们使用,降低了类与类之间的耦合。

2)DI

DI依赖注入:当一个bean对象引用了另外一个bean对象时,spring容器会帮我们创建依赖的bean对象,并注入到另一个bean对象中。有三种注入方式,setter方式注入,构造方法注入,注解注入。

3、依赖注入

1)属性注入

a、setter方法注入
<bean id="userService" class="com.service.UserService">
    <property name="name">
        <value>文学</value>
    </property>
</bean>
b、构造方法注入
<bean id="userService" class="com.service.UserService">
    <constructor-arg index="0" value="wenxue"/>
</bean>
c、注解注入
public class TestService{
    @Autowired
    private UserService userService;//不需要get、set方法
}
<!--启用注解配置,自动注入所需对象-->
<context:annotation-config/>
<bean id="userService" class="com.service.UserService">
    <property name="name">
        <value>文学</value>
    </property>
</bean>

2)集合装配

a、Set
<bean id="userService" class="com.service.UserService">
    <property name="sets">  
        <set>  
            <value>Set1</value>  
            <value>Set2</value>  
            <value>Set3</value>  
        </set>      
    </property> 
</bean>
public class UserService{
    private Set<String> sets=new HashSet<String>();
    public Set<String> getSets(){
        return sets;
    }
    public void setSets(Set<String> sets){
        this.sets=sets;
    }
}
b、List
<bean id="userService" class="com.service.UserService">
    <property name="lists">
        <list>
            <value>list1</value>
            <value>list2</value>
            <value>list3</value>
        </list>
    </property>
</bean>
public class UserService{
    private List<String> lists=new ArrayList<String>();
    public List<String> getLists(){
        return lists;
    }
    public void setLists(List<String> lists){
        this.lists = lists;
    }
}
c、Map
<bean id="userService" class="com.service.UserService">
    <property name="maps">
        <map>
            <entry key="key1" value="Map1"></entry>
            <entry key="key2" value="Map2"></entry>
            <entry key="key3" value="Map3"></entry>
        </map>
    </property>
</bean>
public class UserService{
    private Map<String, String> maps=new HashMap<String, String>();
    public Map<String, String> getmaps(){
        return maps;
    }
    public void setMaps(Map<String, String> maps){
        this.maps = maps;
    }
}
d、prop
<bean id="userService" class="com.service.UserService">
    <property name="properties">
        <props>
            <prop key="key1">Properties1</prop>
            <prop key="key2">Properties2</prop>
            <prop key="key3">Properties3</prop>
        </props>
    </property>
</bean>
public class UserService{
    private Properties properties=new Properties();
    public Properties getProperties(){
        return properties;
    }
    public void setProperties(Properties properties){
        this.properties = properties;
    }
}

3)自动扫描

大的项目中,通常会有上百个组件,如果这些组件采用xml的bean定义来配置,显然会使配置文件显得很臃肿,查找和维护起来不方便。所以引入了组件自动扫描机制,它可以在类路径下寻找标记了@Component@Service@Controller@Repository注解的类,并把这些类纳入到spring容器中管理,它的作用和在xml中使用bean节点配置组件一样。要使用自动扫描机制,我们需要把配置文件如下配置:

<context:component-scan base-package="com.wenxue.service"/>
@Component
public class UserService{

}

所有的 @Repository, @Service@Controller 被注解为 @Component,可以只使用 @Component 对所有组件进行自动扫描。它工作正常,但不是一个好的做法,为便于阅读,应该始终声明@Repository@ Service@Controller 在指定的层,使代码更易于阅读。

4、获取Bean工厂

ApplicationContext继承自BeanFactory接口,用来对Bean进行实例化与依赖关系的装配。

1)通过类路径

ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");

2)通过文件路径

ApplicationContext ac=new FileSystemXmlApplicationContext("src/com/wenxue/beans.xml");

3)通过web应用

WebApplicationContext wac=WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);

三、AOP

1、介绍

1)定义

AOP(aspect oriented programming)面向切面编程:AOP是对OOP的补充和完善,将那些与业务模块无关却被业务模块所共同调用的代码封装起来,集中进行处理,然后利用一种横切的技术应用到目标对象,实现在不增加目标对象代码的前提下,还增加相应功能,减少了系统重复的代码,降低了系统的耦合度,并有利于未来的可维护性和可扩展性。

2)术语

  • 通知:定义了切面的功能,以及何时应用。
  • 连接点:程序应用通知的时机,可以是方法调用时、异常抛出时。
  • 切入点:定义了通知应该应用在哪些连接点,通知将在这些位置执行。
  • 切面:通知和切入点共同组成了切面。
  • 引入:为已存在的类添加新方法和属性。
  • 代理:将通知应用到目标对象后创建的对象。
  • 织入:将切面应用到目标对象从而创建一个新代理对象的过程。

3)通知

  • Around:拦截对目标方法调用
org.aopalliance.intercept.MethodInterceptor 
  • Before:在目标方法调用前调用
org.springframework.aop.MethodBeforeAdvice
  • After:在目标方法调用后调用
org.springframework.aop.AfterReturningAdvice
  • Throws:在目标方法抛出异常时调用
org.springframework.aop.ThrowsAdvice

4)spring对aop的支持

spring中aop代理由spring的ioc容器负责生成、管理,其依赖关系也由ioc容器负责管理。
spring创建代理的规则为:

  • 默认使用java作为动态代理来创建AOP代理,这样会为任何接口实例创建代理。
  • 当需要代理的类不是代理接口时,spring会切换为使用CGLIB

2、应用流程

  • 配置通知:实现接口org.springframework.aop.MethodBeforeAdvice
<bean id="myMethodBeforeAdvice" class="org.springframework.aop.MyMethodBeforeAdvice"/>
  • 配置代理对象
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactory"/>
  • 配置代理接口集
<property name="proxyInterfaces">
    <list>
        <value>com.wenxue.service.TestService</value>
    </list>
</property>
  • 把通知织入到代理对象
<property name="interceptorNames">
    <list>
        <value>myMethodBeforeAdvice</value>
    </list>
</property>
  • 配置被代理对象
<property name="target" ref="testServcieImpl"/>

3、使用

AOP编程的主要目的就是在不增加目标对象原有代码的前提下,还给目标对象增加相应功能。在这里我们就可以知道,我们需要做的就是两件事,一是要编写增加的功能代码,也就是通知;二是配置要增加功能的位置,也就是切入点。

在实际的开发过程中一般有两种AOP的使用,在面向切面编程时,我们使用<aop:aspect/>方式;在进行事务管理时,我们使用<aop:advisor/>方式

1)aspect方式

<aop:aspect/>方式是用来定义切面,是横切逻辑,就是要在连接点上做些什么,可以多次使用,具有可重用性,而且可以有多个切入点和通知,大多用于日志、缓存。这种方式对于通知,我们只需要普通的java bean对象即可。
要增加的功能部分,通知:

public class TimeHandler {
    public void before(){
        System.out.println("方法前:"+System.currentTimeMillis());
    }

    public void after(){
        System.out.println("方法后:"+System.currentTimeMillis());
    }
}

配置增加功能的位置,切入点:

<!-- 要增加的功能,通知 -->
<bean id="timeHandler" class="com.wenxue.study.controller.TimeHandler"/>

<aop:config>
    <!-- 配置切面 -->
    <aop:aspect id="time" ref="timeHandler">
        <aop:pointcut expression="execution(* com.wenxue.study.service.Hello.*(..))" id="helloMethod"/>
        <aop:before method="before" pointcut-ref="helloMethod"/>    
        <aop:after method="after" pointcut-ref="helloMethod"/>
    </aop:aspect>
</aop:config>

从这里我们也可以看出切面正是由切入点和通知组成,<aop:pointcut/>中配置了切入点,在这里是指出了要应用通知的位置是在Hello类中的所有方法里。<aop:before/><aop:after/>则是将切入点和通知联系起来,它表明在我们之前配置的切入点中应用通知,<aop:before/>是表示在目标对象的方法调用前执行通知中的before()方法,也就是我们常说的前置通知;<aop:after/>是表示在目标方法调用后执行通知中的after()方法,也就是我们常说的后置通知。

测试:

public static void main(String[] args) {
    @SuppressWarnings("resource")
    ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
    Hello hello1=(Hello) applicationContext.getBean("helloImp1");
    Hello hello2=(Hello) applicationContext.getBean("helloImp2");
    hello1.firstMethod();
    System.out.println();
    hello1.secondMethod();

    System.out.println();
    hello2.firstMethod();
    System.out.println();
    hello2.secondMethod();
}

2)advisor方式

<aop:advisor/>方式是定义通知器,也就是在哪些切入点上应用什么<aop:aspect/>,这样的好处是让多个横切逻辑(即<aop:aspect/>)多次使用,提供可重用性,一般用于事务处理。这种方式对于通知,我们必须要实现Advice接口。

通知:

public class TimeAdvice implements MethodBeforeAdvice,AfterReturningAdvice{

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("方法后:advice"+System.currentTimeMillis());
    }

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("方法前:advice"+System.currentTimeMillis());
    }
}

切入点:

<bean id="timeAdvice" class="com.wenxue.study.controller.TimeAdvice"/>
<aop:config>
    <aop:pointcut expression="execution(* com.wenxue.study.service.Hello.*(..))" id="helloMethod"/>
    <aop:advisor advice-ref="timeAdvice" pointcut-ref="helloMethod"/>
</aop:config>

这种方式我们可以更清楚的看到,切面由通知和切入点组成,然后使用<aop:advisor/>元素进行联系,来完成我们的AOP编程,实现对目标对象增加新的功能。

4、事务处理

db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://localhost\:3306/ssm
jdbc.username=root
jdbc.password=root

applicationContext-dao.xml

<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 数据源,使用DBCP -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driver" value="${jdbc.driver}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
    <property name="maxActive" value="10"/>
    <property name="maxIdle" value="5"/>
</bean>

applicationContext-transaction.xml

<!-- 事务控制,对mysql操作数据库事务控制,spring使用jdbc的事务控制类 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 配置通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 传播行为 -->
        <tx:method name="save*" propagation="REQUIRED"/>
        <tx:method name="delete*" propagation="REQUIRED"/>
        <tx:method name="insert*" propagation="REQUIRED"/>
        <tx:method name="update*" propagation="REQUIRED"/>
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
</tx:advice>

<!-- 配置aop -->
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.wenxue.service.impl.*.*(..))"/>
</aop:config>

猜你喜欢

转载自blog.csdn.net/qq_31792281/article/details/80063468