Spring

一、Spring介绍

Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。

1、特点

  • 1、方便解耦,简化开发
  • 2、AOP编程的支持
  • 3、声明式事务的支持
  • 4、方便程序的测试
  • 5、方便集成各种优秀框架
  • 6、降低Java EE API的使用难度

2、组织框架

组织框架

3、核心模块

  • spring-core:依赖注入IOC与DI的最基本实现
  • spring-beans:Bean工厂与bean的装配
  • spring-context:spring的context上下文即IoC容器
  • spring-context-support
  • spring-expression:spring表达式语言

二、Spring中的IOC

IOC是 Inverse of Control 的简写,意思是控制反转。是降低对象之间的耦合关系的设计思想。

DI是Dependency Injection的缩写,意思是依赖注入,说的是创建对象实例时,同时为这个对象注入它所依赖的属性。

1、实现过程

1、添加对应依赖包

<!-- Spring的核心工具包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
<!--在基础IOC功能上提供扩展服务,还提供许多企业级服务的支持,有邮件服务、
任务调度、远程访问、缓存以及多种视图层框架的支持-->
    <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.8.RELEASE</version>
    </dependency>
<!-- Spring IOC的基础实现,包含访问配置文件、创建和管理bean等 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
<!-- Spring context的扩展支持,用于MVC方面 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
<!-- Spring表达式语言 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>

2、创建配置文件applicationContext.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">
</beans>

3、在配置文件中创建对象

<bean id="对象名" class="类的完整路径">
	<property name="属性名" ref="对象的id值"></property>
</bean>

4、加载配置文件,获得对象

ApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");//解析xml文件
Users users=(Users)app.getBean("u1");//通过getBean得到指定对象

2、bean标签的属性介绍

属性 说明
class 指定bean对应类的全路径
name name是bean对应对象的一个标识
scope 执行bean对象创建模式和生命周期,scope="singleton"(默认单例)和scope="prototype"(多例)
id id是bean对象的唯一标识,不能添加特别字符
lazy-init 是否延时加载 默认值:false。true 延迟加载对象,当对象被调用的时候才会加载,测试的时候,通过getbean()方法获得对象。lazy-init="false" 默认值,不延迟,无论对象是否被使用,都会立即创建对象,测试时只需要加载配置文件即可。注意:测试的时候只留下id,class属性
init-method 只需要加载配置文件即可对象初始化方法
destroy-method 对象销毁方法

3、Spring中对象创建的方式

1、无参构造

2、有参构造

public Person(String name , Car car){
        this.name = name;
        this.car = car;
        System.out.println("Person的有参构造方法:"+name+car);
    }

	<!--给属性赋值时,对象类型选择ref,非对象类型选择value-->
    <bean name="person" class="com.vinci.spring.bean.Person">
        <constructor-arg name="name" value="rose"/>
        <constructor-arg name="car" ref="car"/>
    </bean>

3、静态方法创建对象

//静态工厂模式
public class PersonFactory {
    public static Person createPerson(){
        System.out.println("静态工厂创建Person");
        return new Person();
    }
}

<bean name="pf" class="com.xzk.PersonFactory" factory-method="createPerson" />

4、非静态工厂方法

public class Users{
    public Person createPerson1(){
        System.out.println("非静态工厂创建Person");
        return new Person();
    }
}

<bean id="u2" class="com.bean.Users"></bean>
<bean id="u3" factory-method="createPerson1" factory-bean="u2"></bean>

4、SpringBean的生命周期【重要】

生命周期

三、DI注入值

分类:一种是调取属性的set方法赋值,第二种使用构造方法赋值

1、set注入值

1、基本属性类型值注入
    <property name="name" value="jeck" />
2、引用属性类型值注入
    <property name="car" ref="car"></property>

2、构造注入

1、通过name属性,按照参数名赋值
    public Person(String name , Car car){
        this.name = name;
        this.car = car;
        System.out.println("Person的有参构造方法:"+name+car);
    }
    <bean name="person" class="com.xzk.spring.bean.Person">
        <constructor-arg name="name" value="rose"/>
        <constructor-arg name="car" ref="car"/>
    </bean>
2、通过index属性,按照参数索引注入
        <bean name="person2" class="com.xzk.spring.bean.Person">
    <constructor-arg name="name" value="helen" index="0"></constructor-arg>
    <constructor-arg name="car" ref="car" index="1"></constructor-arg>
    </bean>
3、使用type注入(一般不使用)
     public Person(Car car, String name) {
            super();
            System.out.println("Person(Car car, String name)");
            this.name = name;
            this.car = car;
        }
        public Person(Car car, Integer name) {
            super();
            System.out.println("Person(Car car, Integer name)");
            this.name = name + "";
            this.car = car;
        }
      <bean name="person2" class="com.xzk.spring.bean.Person">
        <constructor-arg name="name" value="988" type="java.lang.Integer">
        </constructor-arg>
        <constructor-arg name="car" ref="car" ></constructor-arg>
      </bean>   

3、spel spring表达式

<bean name="car" class="com.xzk.spring.bean.Car" >
    <property name="name" value="mime" />
    <property name="color" value="白色"/>
</bean>
<!--利用spel引入car的属性 -->
<bean name="person1" class="com.xzk.spring.bean.Person" p:car-ref="car">
    <property name="name" value="#{car.name}"/>
    <property name="age" value="#{person.age}"/>
</bean>

4、p命名空间注入值

1、首先引用p命名空间【配置文件中 添加命名空间p】
    xmlns:p="http://www.springframework.org/schema/p"
2、进行操作
    <bean id="u6" class="com.entity.Users" p:age="30" p:name="李四" p:student-ref="stu1"></bean>
   基本类型值: p:属性名="值"
   引用类型值: P:属性名-ref="bean名称"

5、复杂类型注入(Object[],list,set,map,java.util.Properties)

<!-- 数组变量注入 -->
    <property name="arrs">
        <list>
            <value>数组1</value>
            <!--引入其他类型-->
            <ref bean="car"/>
        </list>
    </property>
<!-- 集合变量赋值-->
    <property name="list">
        <list>
            <value>集合1</value>
            <!--集合变量内部包含集合-->
            <list>
            <value>集合中的集合1</value>
            <value>集合中的集合2</value>
            <value>集合中的集合3</value>
            </list>
            <ref bean="car" />
        </list>
    </property>
<!-- set赋值-->
     <property name="set">
        <set>
            <value>set</value>
            <!--引入其他类型-->
            <ref bean="car"/>
        </set>
    </property>
<!--map赋值 -->
    <property name="map">
        <map>
            <entry key="car" value-ref="car" />(有ref是对象类型的引用)
            <entry key="name" value="保时捷" />
            <entry key="age" value="11"/>
        </map>
    </property>
<!-- properties赋值 -->
    <property name="properties">
        <props>
            <prop key="name">pro1</prop>
            <prop key="age">111</prop>
        </props>
    </property>

6、自动注入(由程序自动给属性赋值)

autowire:

  • no 不自动装配(默认值)

  • byName 属性名=id名 ,调取set方法赋值

  • byType 属性的类型和id对象的类型相同,当找到多个同类型的对象时报错,调取set方法赋值

  • constructor 构造方法的参数类型和id对象的类型相同,当没有找到时,报错。调取构造方法赋值

<bean id="service" class="service.impl.UserServiceImpl" autowire="constructor">
</bean>
配置全局自动装配:
	<beans default-autowire="constructor/byName/byType/no">

四、注解实现IOC

1、配置文件中添加约束

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

3、配置注解扫描:指定扫描包下所有类中的注解,扫描包时,会扫描包所有的子孙包

<!--扫描包设置-->
<context:component-scan base-package="com.vinci.spring.bean"></context:component-scan>

3、注解

1、添加在类名上
    @Component("对象名")
    @Service("person") // service层
    @Controller("person") // controller层
    @Repository("person") // dao层
    @Scope(scopeName="singleton") //单例对象
    @Scope(scopeName="prototype") //多例对象
2、添加在属性上
    @Value("属性值")
    private String name;
   
	@Autowired //默认使用byType,如果一个接口类型,同时有两个实现类,则报错,此时可以借助@Qualifier("beanname")    
	@Qualifier("bean name")
    private Car car;
    
	//说明:@Resource 是java的注释,但是Spring框架支持,@Resource指定注入哪个名称的对象
    //@Resource(name="对象名") == @Autowired + @Qualifier("name")   
	@Resource(name="baoma")
	private Car car;
3、添加在方法上
   @PostConstruct //等价于init-method属性
    public void init(){
   	 	System.out.println("初始化方法");
    }

    @PreDestroy //等价于destroy-method属性
    public void destroy(){
    	System.out.println("销毁方法");
    } 

五、Aop介绍

AOP(Aspect Oriented Programming)即面向切面编程。即在不改变原程序的基础上为代码段增加新的功能。应用在权限认证、日志、事务。

作用:分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

aop

六、AOP的实现机制

  • JDK 的动态代理:针对实现了接口的类产生代理。InvocationHandler接口
  • CGlib 的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术 生成当前类的子类对象,MethodInterceptor接口

1、JDK动态代理实现

1、创建接口和对应实现类
    public interface UserService {
    	public void login();
    }
	//实现类
    public class UserServiceImpl implements UserService {
    	public void login(){}
    }
2、创建动态代理类,实现InvocationHandler接口
    public class agency implements InvocationHandler {
            private UserService target; //目标对象
            public agency(UserService target){
             this.target = target;
            }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //本方法中的其他输出输入增强
            //proxy 代理方法被调用的代理实例
            System.out.println("方法触发了");
            //执行被代理类 原方法
            Object invoke = method.invoke(target, args);
            System.out.println("执行完毕了");
            return invoke;
        }
    }
3、创建测试
    @Test
    public void test1(){
        //测试JDK动态代理技术
        UserService us = new UserServiceImpl();
        agency ag = new agency(us);
        //这里不能转换成一个实际的类,必须是接口类型
        UserService uservice = (UserService)     Proxy.newProxyInstance(us.getClass().getClassLoader(),us.getClass().getInterfaces(),ag);
        uservice.login();
    }

测试结果: 在调用接口方法的前后都会添加代理类的方法!

2、CGlib实现代理

  • 使用JDK创建代理有一个限制,它只能为接口创建代理实例.这一点可以从Proxy的接口方法newProxyInstance(ClassLoader loader,Class [] interfaces,InvocarionHandler h)中看的很清楚

两种代理方式的区别:

  • 1、jdk动态代理生成的代理类和委托类实现了相同的接口;
  • 2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
  • 3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;