Spring学习笔记
IoC和DI理解
https://www.w3cschool.cn/wkspring/tydj1ick.html spring在线文档
IoC(控制反转)
首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,
就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常
见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………
,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序
开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),
使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男
男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周
杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚
就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一
个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东
西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。
所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体
的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
DI(依赖注入)
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Inje
ction,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection
对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,
A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就
完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的
,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序
在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。
装配bean基于xml
实例化方式
<bean id="beans1" class="helloImpl"/>
<bean>配置需要创建的对象
id:用于之后从spring容器获得实例时使用的
class:需要创建实例的全限定类名
@Test
public void test1(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
helloImpl beans1 = (helloImpl) context.getBean("beans1");
beans1.say();
}
BeanFactor: 这是一个工厂,用于生成任意bean,采取延迟加载,第一次getBean时才会初始化Bean
ApplicationContext:是BeanFactor的子接口,功能更强大(国际化处理,事件传递,bean自动装配,各种不同应用层的context
实现)。当配置文件被加载,就进行对象实例化
ClassPathXmlApplicationContext:用于加载classpath(类路径,src)下的xml
加载xml运行位置-》WEB-INF/class/。。。xml
FileSystemXmlApplicationContext:用于加载指定盘符下的xml
加载xml运行位置-》WEB-INF/。。。xml
通过javaweb ServletContext getRealPath()获得具体盘符
静态工厂创建的实例交予spring
class:确定静态工厂全限定类名
factory-method:确定静态方法
工厂:
public class mybeanfactory{
public static userservice createservice(){
return new userserviceimpl();
}
}
spring配置
将静态工厂创建的实例交予spring
class确定静态工厂全限定类名
factory-method 确定静态方法名
静态工厂(方法静态),测试的时候 不需要new工厂 可以直接工厂.方法
userservice s=mybeanfactory.createservice();
s.adduser();
配置静态工厂的spring xml
<bean id="" class="(工厂的全限定类名)" factory-method="(方法名(可能有很多就写清楚具体方法))"</bean>
实例工厂:需要new
mybeanfactory f= new mybeanfactory();
userservice s=f.createservice();
s.adduser();
配置实例工厂的spring xml
1.创建工厂实例
<bean id="a" class=""/>
2.获取userservice factory-bean 确定工厂实例 factory-method 确定普通方法
<bean id="" factory-bean="a" factory-method="(工厂中具体的方法名)" />
bean的种类
普通bean:之前操作的都是普通bean。 <bean id="" class="A"/>, spring直接创建A的实例并返回
FactoryBean:是一个特殊的bean,具有工厂生成对象的能力,只能生成特定的对象
bean必须实现FactoryBean接口,此接口提供方法, getOeject() 用于获得特定的bean
<bean id="" class="FB"/>先创建FB实例,使用调用getObject()方法,并返回方法的返回值
==》FB fb=new FB();
return fb.getObject();
BeanFactory和FactoryBean对比
BeanFactory:工厂,用于生成任意bean。
FactoryBean:特殊bean用于生成另一个特定的bean,例如:ProxyFactoryBean,此工厂bean用于生产代理。
<bean id="" class="...ProxyFactoryBean" />获得代理对象实例,AOP使用
作用域
作用域:用于确定spring创建bean实例个数
当在 Spring 中定义一个 bean 时,你必须声明该 bean 的作用域的选项。例如,为了强制 Spring 在每次需要时都产生一个新的
bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需要时都返回同一个bean实例,
你应该声明 bean 的作用域的属性为 singleton。
scope配置项有5个属性,用于描述不同的作用域。
<bean id="" class="...impl" scope=" " />
① singleton(单例,默认值)
在spring ioc容器中仅存在一个bean实例,bean以单例方式存在,默认值
② prototype(多例)
每次从容器中调用bean时,都返回一个新的实例,即每次调用getbean()时,相当于执行new xxxbean()
③ request
该属性仅对HTTP请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。
④ session
该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。
⑤ global-session
该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例。
生命周期
初始化和销毁
目标方法执行前、执行后,将进行初始化或销毁
要求:1.容器必须close,销毁方法才能执行
2.必须单例
1. ApplicationContext applicationContext = new ClassPathXmlApplicatonContext(xmlpath)
applicationContext.getClass().getMethod("close").invoke(applicationContext)
//ApplicationContext是接口 接口中没有定义,实现类提供 反射执行实现类
2. ClassPathXmlApplicatonContext applicationContext = new ClassPathXmlApplicatonContext(xmlpath)
applicationContext.close();
<bean id="" class="" init-method="初始化方法名称" destroy-method="销毁的方法名称"/>
BeanPostProcessor后处理Bean
spring提供一种机制,只要实现此接口BeanPostProcessor,并将实现类提供给spring容器,spring容器将自动执行
。在初始化方法前执行before(),在初始化方法后执行after()
spring提供工厂钩子,用于修改实例对象,可以生成代理对象,时aop底层
A a = new A();
a=B.before(a); ----->将a的实例对象传递给后处理bean,可以生成代理对象并返回
a.init();
B.after(a);
a.addUser();
a.destroy();
属性依赖注入
setter注入
<bean id="personid" class="">
<property name="pname" value="XXX"></property>
<property name="age" >
<value>1234</value>
</property>
<property name="homeaddr" ref="aaa"></property>
<bean id="aaa" class="">
<property name="addr" value="XXX"></property>
</bean>
P命名空间(了解)
对“setter方法注入”进行简化,替换<property name="属性名">,而是在
<bean p:属性名="普通值" p:属性名-ref="引用值">
p命名空间使用前提,必须添加命名空间
引用约束:xmlns:p="..../schema/p"
例:<bean id="" class="" p:name="" p:age="" p:homeaddr-ref=""/>
SpEL(了解)
对<property>进行统一编程,所有的内容都使用value
<property name="" value="#{表达式}">
#{123}、#{'jack'} : 数字、字符串
#{beanId} : 另一个bean引用
#{beanId.propName} : 操作数据
#{beanId.toString} : 执行方法
#{T(类).字段|方法} : 静态方法或字段
集合
集合的注入都是给<property>添加子标签
数组: <array>
List: <List>
Set: <set>
Map: <map>
propertis: <props>
普通数据:<value>
引用数据:<ref>
例子:
<bean id="" class="">
<property name="bean的属性">
<array> (<list> <set>同理)
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</array>
</property>
<property name="bean的属性">
<map>
<entry key="" value=""></entry>
</map>
</property>
<property name="bean的属性">
<props>
<prop key="XXX" >XXX</prop>
</props>
</property>
装配bean基于注解
注解:就是一个类,取代@注解名称
开发中:使用注解,取代xml配置文件
需要引入dtd(命名空间的声明)
-------------------------------------------------------------
<?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="test"></context:component-scan>
</beans>
--------------------------------------------------------------
1.@component 取代 <bean class="">
@component("id") 取代 <bean id="" class="">
2.web开发,提供3个@component注解衍生注解(功能一样)
@Repository:dao层
@Service:service层
@Controller:web层
使用前先扫描包
3.依赖注入 给私有字段设置,也可以给setter方法设置
普通值 @Value("")
引用值
方式1:按照【类型】注入
@Autowrired
方式2:按照【名称】注入1
@Autowrired
@Qualifier("名称")
方式3:按照【名称】注入2
@Resource("名称")
@Required
注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中
,否则容器就会抛出一个 BeanInitializationException 异常。下面显示的是一个使用 @Required 注释的示例。
@Qualifier
可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种
情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装配来消除混乱。
AOP
aop术语
1.target:目标类,需要被代理的类,例如:UserService
2.joinpoint:连接点,所谓连接点时值那些可能被拦截到的方法,例如:所有方法
3.poincut:切入点,已经被加强的连接点,例如:add user()
4.advice:通知/增强,增强代码。例如:after、before
5.weaving:织入,指把增强advice应用到目标对象target来创建新的代理对象的过程
6.proxy:代理类
7.aspect:切面,是切入点pointcut和通知advice的结合
一个线时一个特殊的面
一个切入点和一个通知,组成一个特殊的面
手动代理
public static Userservice createservice(){
//1.目标类
Userservice userservice = new UserserviceImpl();
//2.切面类
MyAspect myaspect = new MyAspect();
/* 3.代理类:将目标类(切入点)和切面类(通知)结合--》切面
proxy.newProxyInstance
参数1.loader,类加载器,动态代理类运行时创建,任何类都需要类加载器将其加载到内存
一般情况:当前类:class.getClassLoader();
目标类实例:getClass().get...
参数2.class[] interfaces 代理类需要实现的所有接口
方式1.目标类实例:getClass().getInterfaces(); 注意:只能获得自己的接口,不能获得父类元素的接口
方式2.new Class[]{UserService.class}
例如:jdbc驱动--》DriverManager 获得接口Connection
参数3.InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部类
提供invoke方法,代理类的每一个方法执行时,都将调用一次invoke
参数31:Object proxy:代理对象
参数32:Method method:代理对象当前执行的方法的描述对象(反射)
参数33:Object[] args:方法实际参数
*/
}
手动代理例子
UserService
package com.yjf.JDk;
public interface UserService {
public void add();
public void delete();
public void find();
}
Myaspect
package com.yjf.JDk;
public class Myaspect {
public void after() {
System.out.println("------after");
}
public void before() {
System.out.println("------befor");
}
}
MybeanFactory
package com.yjf.JDk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MybeanFactory {
public static UserService creatservice() {
UserService userService=new UserServiceImpl();
Myaspect myaspect = new Myaspect();
UserService proxyService=(UserService) Proxy.newProxyInstance(
MybeanFactory.class.getClassLoader(),
userService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
myaspect.after();
Object object=method.invoke(userService);
myaspect.before();
return object;
}
}
);
return proxyService;
}
}
test
package com.yjf.JDk;
public class test {
public static void main(String[] args) {
UserService u=MybeanFactory.creatservice();
u.add();
u.delete();
u.find();
}
}
结果
------after
add---------
------befor
------after
delete---------
------befor
------after
find---------
------befor