Spring简介: spring是一个框架,spring是2003兴起的一个轻量级开发框架,由Rod Johnson,
框架的优势:分层架构(MVC M:模型 v:视图 c:控制层)
Spring优势:融合性强(spring相当于一个容器,一个大熔炉),IOC(控制反转,属性注入),Aop(面向切面编程,“oop” Aop就是oop的补充和完善)
IOC&DI
IOC(控制反转)或DI(依赖注入):明确定义组件的接口,独立开发各个组件,然后根据组件的依赖关系组装运行;即将创建及管理对象的权利交给Spring容器。Spring是一个轻型容器(light-weight Container),其核心是Bean工厂(Bean Factory),用以构造我们所需要的M(Model)。能够让相互协作的软件组件保持松散耦合。降低了业务对象替换的复杂性,提高了组件之间的解耦。
IOC(控制反转)
控制:通过spring控制对象的创建
反转:通过反射获得ApplicationContext容器,通过容器转换为类对象
applicationContext&BeanFactory
BeanFactory 接口
spring原始接口.最底层的接口。针对原始接口的实现类功能较为单一
BeanFactory接口实现类的容器.特点是每次在获得对象时才会创建对象,为了节省内存
ApplicationContext
每次容器启动时就会创建容器中配置的所有对象.并提供更多功能
从类路径下加载配置文件:ClassPathXmlApplicationContext
从硬盘绝对路径下加载配置文件:FileSystemXmlApplicationContext(“d:/xxx/yyy/xxx”)
ioc配置详解
4.1Bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd ">
<!--将User对象交给spring容器管理 ,其中name任意,class为全包名
class属性:被管理对象的完整类名
name属性:给被管理的对象起个名字,根据该名称获得对象
可以重复,可以使用特殊字符
id属性:与name属性一模一样
名称不可重复,不能使用特殊字符
结论:尽量使用name属性
-->
<bean name="user" class="com.jinghang.bean.User"></bean>
</beans>
4.2Spring创建对象的三种方式
1.空参构造方式(最主要方式)
<!--创建方式1:空参构造创建 -->
<bean name="user" class="com.jinghang.bean.User"></bean>
//1.创建容器对象,相对于src下的路径
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
工厂模式:工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
2.静态工厂(了解)
public static User createUser() {
System.out.println("静态工厂创建User");//表示由用户自己创建
return new User();
}
<!--创建方式2:静态工厂创建方式
调用UserFactory的createUser方法创建名为user2的对象,放入容器
-->
<bean name="user2"
class="com.jinghang.UserFactory" //类的全包名
factory-method="createUser"></bean> //类的方法
//测试
public void fun2() {
ApplicationContext ac=new ClassPathXmlApplicationContext("com/jinghang/applicationContext.xml");
//2.向容器“要”user对象
User u=(User) ac.getBean("user2");
}
3.实例工厂(了解)
public User createUser2() {//不是静态方法
System.out.println("实例工厂创建User");
return new User();
}
<!--创建方式3:实例工厂创建
调用UserFactory对象的createUser2方法创建名为user3的对象,放入容器
-->
<bean name="user3"
factory-bean="userFactory"
factory-method="createUser2"></bean>
<bean name="userFactory"
class="com.jinghang.UserFactory" ></bean>
public void fun3() {
ApplicationContext ac=new ClassPathXmlApplicationContext("com/jinghang/b_create/applicationContext.xml");
//2.向容器“要”user对象
User u=(User) ac.getBean("user3");
}
scope属性
单例模式:
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
singleton(默认值):单例对象,被标识为单例的对象在spring容器中只会存在一个实例
<bean name="user" class="com.jinghang.bean.User" scope="singleton"></bean>
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//2.向容器“要”user对象
User u=(User) ac.getBean("user");
User u2=(User) ac.getBean("user");
User u3=(User) ac.getBean("user");
User u4=(User) ac.getBean("user");
//3.打印user对象 会发现只有一个实例
System.out.println(u==u3);
System.out.println(u2==u4);
4.3.2生命周期属性(了解)
配置一个方法作为生命周期初始化方法.spring会在对象创建之后立即调用.
init-method
配置一个方法作为生命周期的销毁方法.spring容器在关闭并销毁所有容器中的对象之前调用.
destory-method
<bean name="user" class="com.jinghang.bean.User" scope="singleton" init-method="init" destroy-method="destroy"></bean>
1
//并在User中实现此方法
public void init() {
System.out.println("初始化");
}
public void destroy() {
System.out.println("销毁");
}
属性注入
set方法注入(重要)(前提是set注入之前该对象提供setter方法)
<bean name="user" class="com.jinghang.bean.User" >
<!--值类型注入:为User对象中名为name的属性注入tom作为值-->
<property name="name" value="tom"></property>
<property name="age" value="18"></property>
<!--引用类型注入:为car属性注入下方配置的car对象 car是user中一个对象-->
<property name="car" ref="car"></property>
</bean>
<!--将car对象配置到容器中 -->
<bean name="car" class="com.jinghang.bean.Car">
<property name="name" value="兰博基尼"></property>
<property name="color" value="黄色"></property>
</bean>
构造函数注入
准备带有参数的构造
<bean name="user2" class="com.jinghang.bean.User">
<!-- name属性:构造函数参数名 -->
<!-- index属性:构造函数参数索引 -->
<!-- type属性:构造函数参数类型 -->
<!-- 上述三个属性不必全部出现,根据情况选择即可 -->
<constructor-arg name="name" value="张三" index="0" type="java.lang.String"></constructor-arg>
<constructor-arg name="car" ref="car"></constructor-arg>
</bean>
p名称空间注入(了解)
<!-- p空间注入 走set方法
1.导入p空间名称 xmlns:p="http://www.springframework.org/schema/p"
2.使用p:属性完成注入
|-值类型 : p:属性名="值"
|-对象类型:p:属性名-ref="bean名称"
-->
<bean name="user3" class="com.jinghang.bean.User" p:name="jack" p:age="20" p:car-ref="car">
</bean>
<!--car对象-->
<!--将car对象配置到容器中 -->
<bean name="car" class="com.jinghang.bean.Car">
<property name="name" value="兰博基尼"></property>
<property name="color" value="黄色"></property>
</bean>
注入方式分为:
set方法注入、构造函数注入、p名称空间注入(包含在set方法里边)、接口注入
复杂类型注入
数组,list,map等等
public class CollectionBean {
private Object[] arr;//数组类型注入
private List list;//list/set类型注入
private Map map;//map注入
}
数组
<bean name="cb" class="com.jinghang.c_injection.CollectionBean">
<!-- 如果数组中只准备一个值(对象),直接使用value|ref即可 -->
<!-- 对象中数组名为arr -->
<!-- <property name="arr" value="Tom"></property> -->
<property name="arr">
<array>
<value>tom</value>
<value>jerry</value>
<ref bean="car"/>
</array>
</property>
</bean>
list
<!-- 如果list中只准备一个值(对象),直接使用value|ref即可 -->
<!-- <property name="list" value="Tom"></property> -->
<property name="list">
<list>
<value>tom</value>
<value>Jerry</value>
<ref bean="car"/>
</list>
</property>
map
<property name="map">
<map>
<entry key="1" value="abc"></entry>
<entry key="2" value="def"></entry>
<entry key-ref="car" value-ref="car"></entry>
</map>
</property>
AOP
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
2、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象。
3、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。
4、切入点(pointcut)
对连接点进行拦截的定义。
5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。
6、目标对象
代理的目标对象。
何为切面:
例如
Jdbc
加载驱动
创建连接
----------------------------------------------------------------------------------------
操作数据
-------------------------------------------------------------------------------------------
关闭资源
Spring对AOP的支持
Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:
1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了
2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB
AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:
1、定义普通业务组件。
2、定义切入点,一个切入点可能横切多个业务组件。
3、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作。
所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。
下面给出一个Spring AOP的.xml文件模板,名字叫做aop.xml,之后的内容都在aop.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
</beans>
基于Spring的AOP简单实现
开始讲解用Spring AOP的XML实现方式,先定义一个接口:
public interface Hello{
void printHello();
void doPrint();
}
定义两个接口实现类:
public class HelloImp1 implements Hello{
public void printHello()
{
System.out.println("我是user1");
}
public void doPrint()
{
System.out.println("张三");
return ;
}
}
public class HelloImpl2 implements Hello{
public void printHello()
{
System.out.println("我是user2");
}
public void doPrint()
{
System.out.println("李四");
return ;
}
}
横切关注点,这里是打印时间:
public class TimeHandler
{
public void printTime()
{
System.out.println("CurrentTime = " + System.currentTimeMillis());
}
}
有这三个类就可以实现一个简单的Spring AOP了,看一下aop.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<bean id="helloImpl1" class="com.jinghang.HelloImpl1" />
<bean id="helloImpl2" class="com.jinghang.HelloImpl2" />
<bean id="timeHandler" class="com.jinghang.TimeHandler" />
<aop:config>
<aop:aspect id="time" ref="timeHandler">
<aop:pointcut id="addAllMethod" expression="execution(* com.jinghang.HelloWorld.*(..))" />
<aop:before method="printTime" pointcut-ref="addAllMethod" />
<aop:after method="printTime" pointcut-ref="addAllMethod" />
</aop:aspect>
</aop:config>
</beans>
写一个main函数调用一下:
public static void main(String[] args)
{
ApplicationContext ctx =
new ClassPathXmlApplicationContext("aop.xml");
HelloWorld hw1 = (HelloWorld)ctx.getBean("helloWorldImpl1");
HelloWorld hw2 = (HelloWorld)ctx.getBean("helloWorldImpl2");
hw1.printHelloWorld();
System.out.println();
hw1.doPrint();
System.out.println();
hw2.printHelloWorld();
System.out.println();
hw2.doPrint();
}
看到给Hello接口的两个实现类的所有方法都加上了代理,代理内容就是打印时间。
基于Spring的AOP使用其他细节
1、增加一个横切关注点,打印日志,Java类为:
public class LogHandler
{
public void LogBefore()
{
System.out.println("Log before method");
}
public void LogAfter()
{
System.out.println("Log after method");
}
}
aop.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<bean id="helloWorldImpl1" class="com.jinghang.HelloWorldImpl1" />
<bean id="helloWorldImpl2" class="com.jinghang.HelloWorldImpl2" />
<bean id="timeHandler" class="com.jinghang.TimeHandler" />
<bean id="logHandler" class="com.jinghang.LogHandler" />
<aop:config>
<aop:aspect id="time" ref="timeHandler" order="1">
<aop:pointcut id="addTime" expression="execution(* com.jinghang.HelloWorld.*(..))" />
<aop:before method="printTime" pointcut-ref="addTime" />
<aop:after method="printTime" pointcut-ref="addTime" />
</aop:aspect>
<aop:aspect id="log" ref="logHandler" order="2">
<aop:pointcut id="printLog" expression="execution(* com.jinghang.HelloWorld.*(..))" />
<aop:before method="LogBefore" pointcut-ref="printLog" />
<aop:after method="LogAfter" pointcut-ref="printLog" />
</aop:aspect>
</aop:config>
</beans>
要想让logHandler在timeHandler前使用有两个办法:
(1)aspect里面有一个order属性,order属性的数字就是横切关注点的顺序
(2)把logHandler定义在timeHandler前面,Spring默认以aspect的定义顺序作为织入顺序
2、我只想织入接口中的某些方法
修改一下pointcut的expression就好了:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<bean id="helloWorldImpl1" class="com.jinghang.HelloWorldImpl1" />
<bean id="helloWorldImpl2" class="com.jinghang.HelloWorldImpl2" />
<bean id="timeHandler" class="com.jinghang.TimeHandler" />
<bean id="logHandler" class="com.jinghang.LogHandler" />
<aop:config>
<aop:aspect id="time" ref="timeHandler" order="1">
<aop:pointcut id="addTime" expression="execution(* com.jinghang.HelloWorld.print*(..))" />
<aop:before method="printTime" pointcut-ref="addTime" />
<aop:after method="printTime" pointcut-ref="addTime" />
</aop:aspect>
<aop:aspect id="log" ref="logHandler" order="2">
<aop:pointcut id="printLog" expression="execution(* com.jinghang.HelloWorld.do*(..))" />
<aop:before method="LogBefore" pointcut-ref="printLog" />
<aop:after method="LogAfter" pointcut-ref="printLog" />
</aop:aspect>
</aop:config>
</beans>
表示timeHandler只会织入HelloWorld接口print开头的方法,logHandler只会织入HelloWorld接口do开头的方法。
3、强制使用CGLIB生成代理
前面说过Spring使用动态代理或是CGLIB生成代理是有规则的,高版本的Spring会自动选择是使用动态代理还是CGLIB生成代理内容,当然我们也可以强制使用CGLIB生成代理,那就是里面有一个"proxy-target-class"属性,这个属性值如果被设置为true,那么基于类的代理将起作用,如果proxy-target-class被设置为false或者这个属性被省略,那么基于接口的代理将起作用。