spring概述(一)

一.遇到的问题

1:代码高耦合

public class EmployeeServiceImpl {
      //依赖DAO
      private IEmployeeDAO employeeDAO = new EmployeeDAOImpl();
}

问题:若把IEmployeeDAO的实现改成:EmployeeMapper.
----->简单工厂设计模式
----->把创建对象的职责交给工厂来管理.
到底工厂创建哪一个类的对象呢?我们是配置在配置文件中的(properties/xml).

2:业务代码中处理事务的繁琐问题
对于事务操作,代码冗余,必须在所有需要控制事务的地方,手动用代码完成几乎完全相同的事务控制逻辑,开发效率低下,并且难以方便的处理事务嵌套需求。
如何降低业务逻辑部分之间耦合度,提高程序的可重用性,同时提高开发的效率!—> AOP
主要分散在方法前后,在真正的业务操作前后的代码,我们可以使用面向切面编程来解决.

EmployeeService{
    public void save(...){
      //开启事务
      dao.save(...);
      //提交事务
    }
    public void update(...){
      //开启事务
      dao.update(...);
      //提交事务
    }
}

3:使用表现层和持久层框架的繁琐步骤

二.Spring介绍

什么是spring:
1、Spring是一个轻量级的DI/IoC和AOP容器的开源框架,来源于Rod Johnson 在其著作《Expert one on one J2EE design and development》中阐述的部分理念和原型衍生而来。
2、Spring提倡以”最少侵入”的方式来管理应用中的代码,这意味着我们可以随时安装或卸载Spring。

使用范围:任何Java应用
Spring根本使命:简化Java开发
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,致力于构建轻量级的JavaEE应用

Spring中常见术语:

1、应用程序:是能完成我们所需要功能的成品,比如购物网站、OA系统。
2、框架:是能完成一定功能的半成品,比如我们可以使用框架进行购物网站开发;框架做一部分功能,我们自己做一部分功能,这样应用程序就创建出来了。而且框架规定了你在开发应用程序时的整体架构,提供了一些基础功能,还规定了类和对象的如何创建、如何协作等,从而简化我们开发,让我们专注于业务逻辑开发。
3、非侵入式设计:从框架角度可以这样理解,无需继承框架提供的类,这种设计就可以看作是非侵入式设计,如果继承了这些框架类,就是侵入设计,如果以后想更换框架之前写过的代码几乎无法重用,如果非侵入式设计则之前写过的代码仍然可以继续使用。
4、轻量级及重量级:轻量级是相对于重量级而言的,轻量级一般就是非入侵性的、所依赖的东西非常少、资源占用非常少、部署简单等等,其实就是比较容易使用,而重量级正好相反。
5、POJO:POJO(Plain Old Java Objects)简单的Java对象,它可以包含业务逻辑或持久化逻辑,但不担当任何特殊角色且不继承或不实现任何其它Java框架的类或接口。
6、容器(Container):在日常生活中容器就是一种盛放东西的器具,从程序设计角度看就是装对象的的对象,因为存在放入、拿出等操作,所以容器还要管理对象的生命周期。

Spring的优势:

1.低侵入/低耦合(降低组件之间的耦合度,实现软件各层之间的解耦。)
2.声明式事务管理
3.方便集成其他框架
4.降低JavaEE开发难度
5.Spring框架中包括JavaEE三层的每一层的解决方案 (一站式)

Spring能帮我们做什么:

①.Spring能帮我们根据配置文件创建及组装对象之间的依赖关系。
②.Spring 面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制。
③.Spring能非常简单的帮我们管理数据库事务。
④.Spring还提供了与第三方数据访问框架(如Hibernate、JPA)无缝集成,而且自己也提供了一套JDBC访问模板来方便数据库访问。
⑤.Spring还提供与第三方Web(如Struts1、JSF)框架无缝集成,而且自己也提供了一套Spring MVC框架,来方便web层搭建。
⑥.Spring能方便的与Java EE(如Java Mail、任务调度)整合,与更多技术整合(比如缓存框架)。

Spring框架版本:

Spring2.5:
拥抱驱动编程。
支持SimpleJdbcTemplate的命名参数操作。
Spring3.x:
不再支持JDK1.4;
全面支持泛型。
支持SpEL.
支持WebService的OXM。
Spring4.x:
支持Java8,支持JavaEE6规范。
泛型限定式依赖注入。
对Hibernate4的集成和事务提供更好的管理方案。

Spring框架包分析:

现在都提倡使用Maven去下载Spring的jar:
下载地址:http://repo.spring.io/libs-release-local/org/springframework/spring/

spring-framework-4.x.RELEASE:Spring核心组件。
docs: Spring开发、帮助文档。
libs: Spring核心组件的。jar包、源代码、文档。
schema: Spring配置文件的schema约束文件。

spring-framework-3.x.RELEASE-dependencies:Spring依赖的第三方组件。
包含了各大开源组织提供的依赖jar。比如apache common下的:dbcp.jar pool.jar logging.jar

三.IoC和DI简单介绍

IoC:Inverse of Control(控制反转):

读作“控制反转”,更好理解,不是什么技术,而是一种设计思想,好比于MVC。就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
正控:若调用者需要使用某个对象,其自身就得负责该对象的创建。
反控:调用者只管负责从Spring容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架。
在这里完美地体现了好莱坞法则(Don’t call me ,I’ll call you)。

DI:Dependency Injection(依赖注入):

从字面上分析:
IoC:指将对象的创建权,反转给了Spring容器;
DI :指Spring创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象。
IoC和DI其实是同一个概念的不同角度描述,DI相对IoC而言,明确描述了“被注入对象依赖IoC容器配置依赖对象”。

Container

容器,在生活中容器就是一种盛放东西的器皿,从程序设计角度看作是装对象的对象,因为存在对对象的存入、取出等操作,所以容器还要管理对象的生命周期。

四.案例 helloworld

   //准备jar包(核心包)
   spring-beans-4.2.4.RELEASE.jar
   spring-core-4.2.4.RELEASE.jar
   //报错再添加:
   commons.logging-1.2.jar

创建applicationContext.xml

通过import分模块引入各个模块的bean.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">
    <!--引入各个模块的bean.xml-->
    <import resource="classpath:gzcPro/spring/_01helloSpring/hello.xml"></import>
    <import resource="classpath:gzcPro/spring/_02instance/content/App_context.xml"></import>
</beans>

创建hello.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">

    <!-- Ioc: 控制反转  将class交给spring容器管理-->
    <bean id="helloWorld" class="gzcPro.spring._01helloSpring.HelloWorld">
        <!--DI:依赖注入-->
        <property name="name" value="gzc"></property>
    </bean>
</beans>

创建App_context.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">

    <!-- Ioc: 控制反转  将class交给spring容器管理  
    lazy-init是否在打开spring容器时就加载  false为不延时加载  true为延时加载  
    一般默认为false启动初始化所有的bean 不写-->
    <bean id="someBean" class="gzcPro.spring._02instance.SomeBean" lazy-init="false">
    </bean>

</beans>

创建HelloWorld.java

package gzcPro.spring._01helloSpring;

import lombok.*;

/**
 * Created by Administrator on 2019/8/1.
 */

@AllArgsConstructor
@NoArgsConstructor
@ToString
@Setter
@Getter
public class HelloWorld {
    private String name;

    public void sayHello(){
        System.out.println("hello:"+ name);
    }
}

创建SomeBean.java

package gzcPro.spring._02instance;

import lombok.*;

/**
 * Created by Administrator on 2019/8/1.
 */
@ToString
@Setter
@Getter
public class SomeBean {
    public SomeBean(){
        System.out.println("bean被创建了");
    }
}

创建测试代码APP.java

 @Test
    public void Ioc(){

        //加载spring容器配置文件
        Resource resource = new ClassPathResource("applicationContext.xml");
        //启动spring容器
        BeanFactory factory=new XmlBeanFactory(resource);
        //way-01 getBean 需要强转(不推荐) 容器中可能有多个相同id
        //HelloWorld helloWorld=(HelloWorld) factory.getBean("helloWorld");
        //way-02 getBean 容器中可能有多个相同class(不推荐)
        //HelloWorld helloWorld= factory.getBean(HelloWorld.class);
        //way-03 getBean (推荐)
        HelloWorld helloWorld= factory.getBean("helloWorld",HelloWorld.class);

        System.out.println(helloWorld);
    }

bean被创建了
HelloWorld(name=gzc)

applicationContext.xml 中包含App_context.xml
如下模拟所说,会获取空的构造函数,所以下面代码执行了

 public SomeBean(){ //空的构造函数
        System.out.println("bean被创建了");
    }

Spring管理bean的原理:

什么是BeanFactory:
Spring最基本的接口,表示Spring容器——生产bean对象的工厂,负责配置,创建和管理bean。
备注:bean是Spring管理的单位,在Spring中一切都是bean.

深入Spring管理bean的原理:

1、通过Resource对象加载配置文件;
2、解析配置文件,得到指定名称的bean;
3、解析bean元素,id作为bean的名字,class用于反射得到bean的实例:

模拟spring容器实现:

//模拟spring容器实现
    @Test
    public void test() throws Exception {
        //模拟xml数据
        String className = "gzcPro.spring._01helloSpring.HelloWorld";
        String propertyName = "name";
        String propertyValue = "桂志超";
        //模拟spring操作:IOC 内省
        Class<?> aClass = Class.forName(className);
        //获取空参构造器
        Constructor<?> declaredConstructors = aClass.getDeclaredConstructor();
        //允许访问
        declaredConstructors.setAccessible(true);

        HelloWorld helloWorld = (HelloWorld) declaredConstructors.newInstance();
        //通过内省 赋值DI
        BeanInfo beanInfo = Introspector.getBeanInfo(aClass, Object.class);
        //获取描述器
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor pd:propertyDescriptors) {
            if (propertyName.equals(pd.getName())){
                pd.getWriteMethod().invoke(helloWorld,propertyValue);
            }
        }
        System.out.println(helloWorld);
        helloWorld.sayHello();
    }

HelloWorld(name=gzc)
hello:gzc

说到底,Ioc就是在配置文件中 添加bean对象, DI就是在对象在添加属性值

单元测试说明

    junit4.12  hamcrest-core-1.3.jar
			   junit-4.12.jar
			   
    spring-test-4.2.4.RELEASE.jar. //单元测试
    spring-context-4.2.4.RELEASE.jar. //
    spring-aop-4.2.4.RELEASE.jar. //
    spring-expression-4.2.4.RELEASE.jar. //
    将 spring容器拿出来,不用每次都创建spring容器
/**
 * Created by gzc on 2019/8/1.
 */
@RunWith(SpringJUnit4ClassRunner.class)//引入spring单元测试
@ContextConfiguration("classpath:applicationContext.xml")//引入配置文件
public class App {

    //自动从spring容器中获取这个bean
    @Autowired
    private HelloWorld helloWorld;

    @Test
    public void  test1() throws Exception {
        System.out.println(helloWorld);
    }
}

@Autowired 自动从spring容器中获取这个bean(bean是存在的)

@RunWith(SpringJUnit4ClassRunner.class)//引入spring单元测试
@ContextConfiguration(“classpath:applicationContext.xml”)//引入配置文件
@Autowired
private HelloWorld helloWorld;
取代了----------->
//加载spring容器配置文件
Resource resource = new ClassPathResource(“applicationContext.xml”);
//启动spring容器
BeanFactory factory=new XmlBeanFactory(resource);
HelloWorld helloWorld= factory.getBean(“helloWorld”,HelloWorld.class);

IoC容器:BeanFactory和ApplicationContext对象

BeanFactory:是Spring中最底层的接口,只提供了最简单的IoC功能,负责配置,创建和管理bean。
在应用中,一般不使用BeanFactory,而推荐使用ApplicationContext(应用上下文),原因如下。
BeanFactory和ApplicationContext的区别:
1、ApplicationContext继承了BeanFactory,拥有了基本的IoC功能;
2、除此之外,ApplicationContext还提供了以下的功能:
①、支持国际化;
②、支持消息机制;
③、支持统一的资源加载;
④、支持AOP功能;

package gzcPro.spring._02instance.content;

import ...

/**
 * Created by gzc on 2019/8/1.
 */
//@RunWith(SpringJUnit4ClassRunner.class)//引入单元测试
//@ContextConfiguration("classpath:applicationContext.xml")//引入配置文件
public class App {

    //自动从spring容器中获取这个bean
//    @Autowired
//    private ApplicationContext context;


    @Test
    public void testAppCtx(){
        ApplicationContext context = new ClassPathXmlApplicationContext("gzcPro/spring/_02instance/content/App_context.xml");
        SomeBean someBean1=context.getBean("someBean",SomeBean.class);
        SomeBean someBean2=context.getBean("someBean",SomeBean.class);
        System.out.println(someBean1==someBean2);
    }
}

ApplicationContext context = new ClassPathXmlApplicationContext(“gzcPro/spring/_02instance/content/App_context.xml”);
开启App_context.xml spring容器中的所有bean
---------------------------------------------------》换成
@ContextConfiguration(“App_context.xml”)//引入配置文件
或者(@ContextConfiguration(“classpath:applicationContext.xml”)//引入配置文件)
@Autowired
private ApplicationContext context;

/**
 * Created by gzc on 2019/8/1.
 */
@RunWith(SpringJUnit4ClassRunner.class)//引入单元测试
//@ContextConfiguration("classpath:applicationContext.xml")//引入配置文件
@ContextConfiguration("App_context.xml")//引入配置文件
public class App {

    //自动从spring容器中获取这个bean
    @Autowired
    private ApplicationContext context;


    @Test
    public void testAppCtx(){
        SomeBean someBean1=context.getBean("someBean",SomeBean.class);
        SomeBean someBean2=context.getBean("someBean",SomeBean.class);
        System.out.println(someBean1==someBean2);
    }
}

五.bean

bean的实例化方式:

①.构造器实例化(无参数构造器),最标准,使用最多
②.静态工厂方法实例化:解决系统遗留问题

 <!-- Ioc: 控制反转  将class交给spring容器管理-->
    <!-- 使用静态工厂 -->
    <bean id="someBean1" class="gzcPro.spring._02instance.static_factory.SomeBeanFactory"
     factory-method="getObject">
    </bean>
//静态工厂
public class SomeBeanFactory {
    public static SomeBean getObject(){
        return new SomeBean();
    }

}

    //自动从spring容器中获取这个bean
    @Autowired
    private ApplicationContext context;
    
    @Test
    public void testAppCtx() throws Exception{
        context.getBean("someBean1");
    }

    @Test
    public void testOld() throws Exception{
        SomeBean someBean=SomeBeanFactory.getObject();
    }

③.实例工厂方法实例化:解决系统遗留问题

 <!-- 使用实例工厂-->
    <bean id="factory"  class="gzcPro.spring._02instance.factory.SomeBeanFactory" />
    <bean id="someBean" factory-bean="factory" factory-method="getObject" />
public class SomeBeanFactory implements FactoryBean<SomeBean>{
    public  SomeBean getObject(){
        return new SomeBean();
    }
}    
//自动从spring容器中获取这个bean
    @Autowired
    private ApplicationContext context;

    @Test
    public void testAppCtx() throws Exception{
        System.out.println(context.getBean("someBean"));
    }

    @Test
    public void testOld() throws Exception{
        SomeBeanFactory factory=new SomeBeanFactory();
        SomeBean someBean= factory.getObject();
    }

④.实现FactoryBean接口实例化:实例工厂变种:
集成其他框架使用:SqlSessionFactoryBean和MapperFactoryBean

<!-- FactroyBean创建 -->
<bean id="someBean"  class="gzcPro.spring._02instance.instance_factory.SomeBeanFactory" />
public class SomeBeanFactory implements FactoryBean<SomeBean>{

    public  SomeBean getObject(){
        return new SomeBean();
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    //bean是否单例
    @Override
    public boolean isSingleton() {
        return false;
    }
}
bean作用域

1.单例
2.多例

bean对象的作用域(bean对象可以存活的时间):
<bean id="" class="" scope="作用域"/>
singleton: 单例 ,在Spring IoC容器中仅存在一个Bean实例 (默认的scope)
prototype: 多例 ,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时 ,相当于执行new XxxBean():不会在容器启动时创建对象
request: 用于web开发,将Bean放入request范围 ,request.setAttribute(“xxx”) , 在同一个request 获得同一个Bean
session: 用于web开发,将Bean 放入Session范围,在同一个Session 获得同一个Bean
globalSession: 一般用于Porlet应用环境 , 分布式系统存在全局session概念(单点登录),如果不是porlet环境,globalSession 等同于Session
在开发中主要使用 scope=“singleton”、 scope=“prototype”
对于Struts2中的Action使用prototype类型,其他使用singleton
Spring容器会管理Action对象的创建,此时把Action的作用域设置为prototype.

bean的生命周期 ( init destroy )
<?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"
       default-init-method="init"
       default-destroy-method="destroy">

    <!-- Ioc: 控制反转  将class交给spring容_03scopecope="prototype"多例-->
    <!-- Ioc: 控制反转  将class交给spring容器管理 
    init-method="init" 
    destroy-method="destroy"-->
    <bean id="someBean" class="gzcPro.spring._03scope.SomeBean">
    </bean>
</beans>
public class SomeBean {
    public SomeBean(){
        System.out.println("bean被创建了");
    }

    public void init() {
        System.out.println("初始化");
    }

    public void destroy() {
        System.out.println("被销毁");
    }

    public void doWorlk() {
        System.out.println("工作");
    }
}
@RunWith(SpringJUnit4ClassRunner.class)//引入单元测试
@ContextConfiguration("App_context.xml")//引入配置文件
public class App {

    //自动从spring容器中获取这个bean
    @Autowired
    private ApplicationContext context;

    @Test
    public void testAppCtx(){
        SomeBean someBean1=context.getBean("someBean",SomeBean.class);
        SomeBean someBean2=context.getBean("someBean",SomeBean.class);
        System.out.println(someBean1==someBean2);
        someBean1.doWorlk();
    }
}

bean被创建了
初始化
true
工作
被销毁

六.依赖注入DI及简化Ioc

依赖注入

DI:Dependency Injection: 依赖注入
从字面上分析:
IoC:指将对象的创建权,反转到Spring容器;
DI :指Spring创建对象的过程中,将对象依赖属性通过配置进行注入
其实它们是同一个概念的不同角度描述。DI相对IoC而言,明确描述了“被注入对象依赖IoC容器配置依赖对象”。

对象注入的方式:

1):自动装配(了解)
2):手动注入(重点)
3):注解注入(重点)
通过哪些手段可以注入:
1):可以通过属性注入(setter方法).
2):通过构造器注入.
3):通过SpEL注入.
注入的时候,可有注入哪些类型的值.
1:简单类型(单一的值,使用value属性).
2:复合类型(JavaBean类型,使用ref属性)
3:集合类型(多个值,使用对应的集合标签)

使用setter注入(属性注入):

1,使用bean元素的property子元素设置;
1,简单类型值,直接使用value赋值;
2,复合类型,使用ref赋值;
3,集合类型,直接使用对应的集合类型元素即可。

2,spring通过属性的setter方法注入值;
3,在配置文件中配置的值都是string,spring可以自动的完成类型的转换
4,属性的设置值是在init方法执行之前完成的
5,改进spring的测试,直接在测试类里面注入需要测试的对象

使用DataSource(druid)的案例,给数据源注入Property中的数据.

准备:
MySQL驱动包和Druid连接池包.
druid-1.0.15.jar
mysql-connector-java-8.0.15.jar

  <context:property-placeholder location="db.properties" system-properties-mode="NEVER">
  </context:property-placeholder>
    <!-- 配置数据源-->
    <bean id="dataSurce" class="com.alibaba.druid.pool.DruidDataSource" 
    init-method="init" destroy-method="close">
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>

db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username=root
password=123456

自动装配方式(不推荐):
设置:<bean />元素的:autowire属性:
<bean id="somebean" class="SomeBean全限定名" autowire="byType"/>
autowire属性:让spring按照一定的方式自己去找合适的对象,并完成DI
- default:不要自动注入
- no:不要自动注入
- byName:按照名字注入(按照属性的名字在spring中找bean) factory.getBean(“属性的名字”)
- byType:按照依赖对象的类型注入(factory.getBean(属性的类型))
- constructor:按照对象的构造器上面的参数类型注入
注意:
1,如果按照byName自动注入,要求所有的属性名字和id的名字必须保证一种规范的命名方式;
2,如果按照byType注入,如果spring中同一个类型有多个实例–>报bean不是唯一类型错误;

注解注入

 <!-- 开始DI注解解析程序-->
 <context:annotation-config/>

Spring提供的装配标签:
Autowired和Qualifier标签:
1.通过@Autowired标签可以让Spring自动的把属性需要的对象从Spring容器找出来,并注入给该属性。

2.第三方程序:Spring3.0之前,需要手动配置@Autowired解析注解程序,Spring就会自动的加入针对@Autowired标签的解析程序。从Spring3.0开始,可以不再需要改配置了。如果不在Spring的测试环境中,也找到@Autowired的解析代码,此时页必须配置.

3.@Autowired标签贴在字段或者setter方法上。

4.@Autowired可以同时为一个属性注入多个对象。
public void setXxx(OtherBean1 other1,OtherBean2 other2) {}

5.使用@Autowired标签可以注入Spring内置的重要对象,比如BeanFactory,ApplicationContext。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SpringTest {
@Autowired
private ApplicationContext ctx;
}

6.默认情况下@Autowired标签必须要能找到对应的对象,否则报错。不过,可使用required=false来避免该问题:@Autowired(required=false)

7.@Autowired找bean的方式:
1)、首先按照依赖对象的类型找,如果找到则使用setter方法或者字段直接注入;
2)、如果在Spring上下文中找到多个匹配的类型,再按照名字去找,如果没有匹配则报错;
3)、可以通过使用@Qualifier(“otherBean”)标签来规定依赖对象按照bean的id+类型去找

@ToString
public class SomeBean {
    @Autowired
    @Qualifier("otherBean")  //<bean id=otherBean
    private OtherBean otherBean;
    @Resource
    private OtherBean2 otherBean2;
}

JavaEE提供的装配标签:

@Resource标签:
1,@Resource标签是JavaEE规范的标签;
2,@Resource标签也可以作用于字段或者setter方法;, 效果是跟autowired是一样的
3,也可以使用@Resource标签注入一些spring内置的重要对象,比如BeanFactory.ApplicationContext;
4,@Resource必须要求有匹配的对象;
5,context:annotation-config/既引入了@Autowired标签的解析器,也引入了@Resource的解析器;
6,@Resource标签找bean的方式:
1),首先按照名字去找,如果找到,就使用setter或者字段注入;
2),如果按照名字找不到,再按照类型去找,但如果找到多个匹配类型,报错;
3),可以直接使用name属性指定bean的名称;但是,如果指定的name,就只能按照name去找,如果找不到,就不会再按照类型去找

@Resource(name="otherBean2")
private OtherBean2 other2;
等价于
@Autowired
@Qualifier("otherBean2")
private OtherBean2 other2;

@Autowired VS @Resource:

1,@Autowired:是Spring定义的标签,所以不太稳定,并且对象和spring框架关联(Spring对我们的代码有侵入);
2,@Resouce:是J2EE的规范,所以稳定,在J2EE规范容器中也能正常使用;
在开发中两个注解都是一样的作用,所有选择谁都可以,完全取决于项目经理的选择

使用标签简化IoC:

1.使用标签来完成IoC,就必须有IoC标签的解析器:
使用context:component-scan来扫描spring需要管理的bean
base-package就告诉spring,去哪些包及其子包里去扫描bean,如果有多个包需要被扫描;只需要用逗号隔开多个包即可
<context:component-scan base-package=“com._520it.oa.dao,cn.com._520it.service” />
2.标注Bean的注解:@Component
默认情况,直接使用类的名字(首字母小写作为bean的名字)
如果要修改bean的名称;直接使用value属性来重新定义bean的名称
@Component(“otherbean”)
public class OtherBean {}
3.使用@Component的限制:
1),不能运用到静态工厂方法和实例工厂方法,但是可以使用到FactoryBean;
2),对于没有源代码的类(框架内部的预定义类),只能用XML配置;
4.bean组件版型标签
bean组件版型:
@Service用于标注业务层组件、
@Controller用于标注控制层组件(如struts中的action)
@Repository用于标注数据访问组件,即DAO组件。
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

5.指定bean的作用域:@Scope(“prototype”)
6.初始化和销毁方法
@PostConstruct
public void init() {
相当于
@PreDestroy
public void destory() {
相当于

7.选用xml还是注解:
1),Annotation:使用方便,XML文件很小,但是,依赖关系又重新回到了代码当中;
2),XML:使用稍微复杂,但是,代码很干净,代码不会很任何框架产生关系;XML安全;
两种方式都必须掌握;

    <!-- 开始DI注解解析程序 如果不在Spring的测试环境中,也找到@Autowired的解析代码,此时页必须配置-->
    <context:annotation-config/>
    <!-- 限定属于哪个包中-->
    <context:component-scan base-package="com.gjd.domain"></context:component-scan>
@Component
public class User {
    private Long id;
    private String name;
    private BigDecimal salary;
//    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date hiredate;

    public void sayhello(){
        System.out.println("gjd");
    }
}
 @Autowired
    private User user;

七.代理及AOP简介、配置

增强功能

事务开启,提交和回滚代码

public class TransactionManager {
    public void begin(){
        System.out.println("开启一个事务");
    }

    public void commit(){
        System.out.println("提交一个事务");
    }

    public void rollback(){
        System.out.println("回滚一个事务");
    }
}
public class EmployeeServiceImpl implements IEmployeeService {
    @Override
    public void save() {
        System.out.println(1/0);
        System.out.println("保存一个员工");
    }
    
    @Override
    public void update() {
        System.out.println("更新一个员工");
    }
}

装饰设计模式

装饰设计模式:在不必改变源代码基础上,动态地扩展一个对象的功能。
它是通过创建一个包装对象,也就是包裹真实的对象。
说的直白点,就是对已有对象进行功能增强!
1.装饰模式思想
2.装饰模式实现

得定义一个类(EmployeeService的包装类):
目的:增强EmployeeServiceImpl中的save和update方法.
做什么增强:
1:在调用save方法之前:开启事务:
2:正常调用完save方法之后:提交事务:
3:如果调用save方法出现异常:回滚事务:

//参数构造函数 !importent
@AllArgsConstructor
public class EmployeeServiceWrapper implements IEmployeeService{

    private IEmployeeService employeeService;
    private TransactionManager transactionManager;

    @Override
    public void save() {
        try {
            transactionManager.begin();
            employeeService.save();
            transactionManager.commit();
        }catch (Exception e){
            e.printStackTrace();
            transactionManager.rollback();
        }
    }

    @Override
    public void update() {
        try {
            transactionManager.begin();
            employeeService.update();
            transactionManager.commit();
        }catch (Exception e){
            e.printStackTrace();
            transactionManager.rollback();
        }
    }
}
public class App {
    @Test
    public void  testApp() throws Exception {
        IEmployeeService service=new EmployeeServiceImpl();
        TransactionManager transactionManager=new TransactionManager();
        EmployeeServiceWrapper wrapper=new EmployeeServiceWrapper(service,transactionManager);
        wrapper.save();
        wrapper.update();
    }
}

装饰设计模式在这里存在的问题

1):确实可以这个解决问题.
2):会暴露真实对象–不安全.
3):需要为每一个需要增强的类定义一个包装类

静态代理

//静态代理类
public class EmployeeServiceStaticProxy implements IEmployeeService{
    //没有上面的构造函数,需要添加setter方法     !importent
    @Setter
    private IEmployeeService target;//真实对象
    @Setter
    private TransactionManager transactionManager;

    @Override
    public void save() {
        try {
            transactionManager.begin();
            target.save();
            transactionManager.commit();
        }catch (Exception e){
            e.printStackTrace();
            transactionManager.rollback(e);
        }
    }

    @Override
    public void update() {
        try {
            transactionManager.begin();
            target.update();
            transactionManager.commit();
        }catch (Exception e){
            e.printStackTrace();
            transactionManager.rollback(e);
        }
    }
}
  <!-- 事务代理-->
       <bean id="transactionManager" class="gzcPro.spring._09._staticproxy.manager.TransactionManager"/>

       <!-- 静态代理-->
       <bean id="employeeServiceStaticProxy" class="gzcPro.spring._09._staticproxy.proxy.EmployeeServiceStaticProxy">
              <property name="transactionManager" ref="transactionManager"></property>
              <property name="target">
                     <bean id="employeeService" class="gzcPro.spring._09._staticproxy.service.EmployeeServiceImpl"/>
              </property>
       </bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("App-context09.xml")
public class App {

    @Autowired
    private IEmployeeService proxy;
    @Test
    public void  test() throws Exception {
        proxy.save();
    }
}

静态代理(proxy):在程序运行前就已经存在代理类的字节码文件,代理对象和真实对象的关系在程序运行前就确定了。
静态代理优缺点:
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。
缺点:
1.代理对象的某个接口只服务于某一种类型的对象,也就是说每一个真实对象都得创建一个代理对象。
2.如果需要代理的方法很多,则要为每一种方法都进行代理处理。
3.如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
如果期望一个代理类就可以代理多个真实对象------->动态代理.

JDK动态代理

/**
 * Created by gzc on 2019/8/5.
 */
public class TransactionHandler implements InvocationHandler {

    @Setter
    private TransactionManager manager;
    @Setter
    private Object target;//真是对象

    //获取代理对象
    public <T> T getProxy(){
        System.out.println(target.getClass());
        return (T) Proxy.newProxyInstance(
                TransactionHandler.class.getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    /**
     * h.invoke   (h:this)
     *实现代理功能的方法
     *proxy 代理的对象
     * method 代理方法 update 或者save 当前执行的代理方法
     * args 代理方法的参数
     *
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = null;
        //事务的开启
        try {
            manager.begin();
            ret = method.invoke(target,args);
            manager.commit();
        }catch (Exception e) {
            e.printStackTrace();
            manager.rollback(e);
        }

        return ret;
    }
}

<!-- 事务代理-->
       <bean id="transactionManager" class="gzcPro.spring._09._staticproxy.manager.TransactionManager"/>

       <!-- 静态代理-->
       <bean id="transactionHandler" class="gzcPro.spring._09.proxyHander.TransactionHandler">
              <property name="manager" ref="transactionManager"></property>
              <property name="target">
                     <!--private Object target;//真对象-->
                     <bean class="gzcPro.spring._09._staticproxy.service.EmployeeServiceImpl"/>
              </property>
       </bean>
 @Autowired
    private TransactionHandler handler;
    @Test
    public void  testSave() throws Exception {
        IEmployeeService proxy = handler.getProxy();
        System.out.println(proxy.getClass());
        proxy.save();
    }

动态代理类是在程序运行期间由JVM通过反射等机制动态的生成的,所以不存在代理类的字节码文件。代理对象和真实对象的关系是在程序运行事情才确定的。
JDK动态代理API分析:(只能对接口进行代理)
1、java.lang.reflect.Proxy 类:
Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
主要方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler hanlder)
方法职责:为指定类加载器、一组接口及调用处理器生成动态代理类实例
参数:
loader :类加载器
interfaces :模拟的接口
hanlder :代理执行处理器
返回:动态生成的代理对象
2、java.lang.reflect.InvocationHandler接口:
public Object invoke(Object proxy, Method method, Object[] args)
方法职责:负责集中处理动态代理类上的所有方法调用
参数:
proxy :生成的代理对象
method :当前调用的真实方法对象
args :当前调用方法的实参
返回: 真实方法的返回结果
jdk动态代理操作步骤
① 实现InvocationHandler接口,创建自己增强代码的处理器。
② 给Proxy类提供ClassLoader对象和代理接口类型数组,创建动态代理对象。
③ 在处理器中实现增强操作。

JDK动态代理存在的问题:

JDK动态代理:
1,代理的对象必须要实现接口;
2,需要为每个对象创建代理对象;
3,动态代理的最小单位是类(所有类中的方法都会被处理);

CGLIB动态代理

public class TransactionHandler implements org.springframework.cglib.proxy.InvocationHandler {

    @Setter
    private TransactionManager manager;
    @Setter
    private Object target;//真是对象

    //获取代理对象
    public <T> T getProxy(){

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }

    /**
     * h.invoke   (h:this)
     *实现代理功能的方法
     *proxy 代理的对象
     * method 代理方法 update 或者save 当前执行的代理方法
     * args 代理方法的参数
     *
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = null;
        //事务的开启
        try {
            manager.begin();
            ret = method.invoke(target,args);
            manager.commit();
        }catch (Exception e) {
            e.printStackTrace();
            manager.rollback(e);
        }

        return ret;
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("App-context09.xml")
public class App {

    @Autowired
    private TransactionHandler handler;
    @Test
    public void  testSave() throws Exception {
        IEmployeeService proxy = handler.getProxy();
        //class gzcPro._gzc01._09._staticproxy.service.EmployeeServiceImpl$$EnhancerByCGLIB$$333160c2
        System.out.println(proxy.getClass());
        proxy.save();
    }

    @Test
    public void  testUpdate() throws Exception {
        IEmployeeService proxy = handler.getProxy();
        //class gzcPro._gzc01._09._staticproxy.service.EmployeeServiceImpl$$EnhancerByCGLIB$$333160c2
        System.out.println(proxy.getClass());
        proxy.update();
    }

}

原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
CGLIB代理总结:
1,CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
2,要求类不能是final的,要拦截的方法要是非final、非static、非private的。
3,动态代理的最小单位是类(所有类中的方法都会被处理);

在Spring中:
若目标对象实现了若干接口,Spring就会使用JDK动态代理。
若目标对象没有实现任何接口,Spring就使用CGLIB库生成目标对象的子类。
对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,也更符合面向接口编程规。
cglib和javassist代理的机制都是一样的,都是通过继承实现的.

在上述的操作中,已经实现了动态的为目标对象做功能增强?
为什么还要学Spring的AOP呢?
原因之一是:在为N个service提供代理的时候,我们需要在xml中配置N次

AOP
package gzcPro.spring._09._staticproxy.manager;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * Created by gzc on 2019/8/4.
 */

@Component
@Aspect
public class TransactionManager {

    @Pointcut("execution(* gzcPro.spring._09._staticproxy.service.*Service.*(..))")
    public void pc(){

    }
//    @Before("pc()")
    public void begin(){
        System.out.println("开启一个事务");
    }
//    @AfterReturning("pc()")
    public void commit(){
        System.out.println("提交一个事务");
    }
//    @AfterThrowing(value = "pc()",throwing = "ex")
    public void rollback(Throwable ex){
        System.out.println("回滚一个事务"+ex.getMessage());
    }
//    @After("pc()")
    public void close(){
        System.out.println("关闭一个事务");
    }


    @Around("pc()")//@Before("pc()") @AfterReturning("pc()") @AfterThrowing(value = "pc()",throwing = "ex")@After("pc()")
    public Object  allInOne(ProceedingJoinPoint point){
        Object ret = null ;
        try {
            begin();
            ret = point.proceed();
            commit();

        } catch (Throwable throwable) {
            rollback(throwable);

        } finally {
            close();
        }

        return ret;


    }
}

AOP的目的:

AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,
便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
说人话:把业务方法中与业务无关的操作抽离到不同的对象的方法中,最后使用动态代理的方式组合起来

AOP的优势:

降低模块的耦合度、使系统容易扩展、更好的代码复用性.
Spring的AOP使用动态代理实现:
如果一个类实现了接口,那么spring就使用JDK的动态代理完成AOP;
如果一个类没有实现接口,那么spring就是用cglib完成AOP;

AOP当中的概念:
1、切入点(Pointcut):在哪些类,哪些方法上切入(where);: [在employeServiceImpl 类—>save方法]
2、增强(Advice): 早期翻译为通知,在方法执行的什么时机(when:方法前/方法后/方法前后)做什么(what:增强的功能);[在save方法执行前(when),开启事务(what)]
3、切面(Aspect): 切面=切入点+增强,通俗点就是:在什么时机,什么地点,做什么增强!
[在employeServiceImpl 类—>save方法(where)执行前(when)做开启事务操作(what)]
4、织入(Weaving): 把切面加入到对象,并创建出代理对象的过程。(该过程由Spring来完成)。

XML配置AOP

Spring配置AOP的准备工作:
1).引入Spring AOP开发依赖的jar包:
spring-aop-4.2.4.RELEASE.jar
aopalliance-1.0.0.jar
aspectjweaver-1.8.7.jar

各种不同的增强:

aop:before(前置增强):在方法执行之前执行增强;
aop:after-returning(后置增强):在方法正常执行完成之后执行增强(中间没有遇到任何异常);
aop:after-throwing(异常增强):在方法抛出异常退出时执行增强代码;
aop:after(最终增强):在方法执行之后执行,相当于在finally里面执行;可以通过配置throwing来获得拦截到的异常信息
aop:around(环绕增强):最强大的一种增强类型。 环绕增强可以在方法调用前后完成自定义的行为,环绕通知有两个要求,
1,方法必须要返回一个Object(返回的结果)
2,方法的第一个参数必须是ProceedingJoinPoint(可以继续向下传递的切入点)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

       <!-- 开始DI注解解析程序-->
       <context:annotation-config/>
       <!--<context:component-scan base-package="gzcPro.spring._09._staticproxy"></context:component-scan>-->
       <!--<aop:aspectj-autoproxy/>-->
       <!-- 事务管理对象-->
       <bean id="txManager" class="gzcPro.spring._09._staticproxy.manager.TransactionManager"></bean>
       <!-- 需要增强的真实类-->
       <bean id="employeeService" class="gzcPro.spring._09._staticproxy.service.EmployeeServiceImpl"></bean>

       <aop:config>
              <aop:aspect ref="txManager">
                     <aop:pointcut id="px" expression="execution(* gzcPro.spring._09._staticproxy.service.*Service.*(..))"/>
                     <!--之前-->
                     <aop:before method="begin" pointcut-ref="px"></aop:before>
                     <!--没异常-->
                     <aop:after-returning method="commit" pointcut-ref="px"></aop:after-returning>
                     <!--有异常-->
                     <aop:after-throwing method="rollback" pointcut-ref="px" throwing="ex"></aop:after-throwing>
                     <!--finally-->
                     <aop:after method="close" pointcut-ref="px"></aop:after>

                     <!--环绕增强 将所有的增强合并-->
                     <!--<aop:around method="allInOne" pointcut-ref="px"></aop:around>-->
              </aop:aspect>
       </aop:config>

</beans>

或者

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

       <!-- 开始DI注解解析程序-->
       <context:annotation-config/>
       <!--<context:component-scan base-package="gzcPro.spring._09._staticproxy"></context:component-scan>-->
       <!--<aop:aspectj-autoproxy/>-->
       <!-- 事务管理对象-->
       <bean id="txManager" class="gzcPro.spring._09._staticproxy.manager.TransactionManager"></bean>
       <!-- 需要增强的真实类-->
       <bean id="employeeService" class="gzcPro.spring._09._staticproxy.service.EmployeeServiceImpl"></bean>

       <aop:config>
              <aop:aspect ref="txManager">
                     <aop:pointcut id="px" expression="execution(* gzcPro.spring._09._staticproxy.service.*Service.*(..))"/>
                     <!--环绕增强 将所有的增强合并-->
                     <aop:around method="allInOne" pointcut-ref="px"></aop:around>
              </aop:aspect>
       </aop:config>

</beans>

或者

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

       <!-- 开始DI注解解析程序-->
       <context:annotation-config/>
       <!--@Component-->
       <context:component-scan base-package="gzcPro.spring._09._staticproxy"></context:component-scan>
       <!--@Aspect-->
      <aop:aspectj-autoproxy/>
</beans>
 @Autowired
    private IEmployeeService service;
    @Test
    public void  testSave() throws Exception {
        System.out.println(service.getClass());
        service.save();
    }

    @Test
    public void  testUpdate() throws Exception {
        service.update();
    }

class com.sun.proxy.$Proxy16
开启一个事务
回滚一个事务/ by zero
关闭一个事务

开启一个事务
更新一个员工
提交一个事务

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值