概述
Spring |
|
Spring
第一天:
一,软件应用分层架构
标准三层架构:
1:数据访问层:主要是对原始数据(数据库或者文本文件等存放数据的形式)的操作层,而不是指原始数据,也就是说,是对数据的操作,而不是数据库,具体为业务逻辑层或表示层提供数据服务.
2:业务逻辑层:主要是针对具体的问题的操作,也可以理解成对数据层的操作,对数据业务逻辑处理,如果说数据层是积木,那逻辑层就是对这些积木的搭建。具体的讲主要负责对数据层的操作。也就是说把一些数据层的操作进行组合。
3:表示层:主要表示WEB方式,如果逻辑层相当强大和完善,无论表现层如何定义和更改,逻辑层都能完善地提供服务。主要对用户的请求接受,以及数据的返回,为客户端提供应用程序的访问。
优点:
1:开发人员可以只关注整个结构中的其中某一层;
2:可以很容易的用新的实现来替换原有层次的实现;
3:可以降低层与层之间的依赖;
4:有利于标准化;
5:利于各层逻辑的复用。组件
6:结构更加的明确
7:在后期维护的时候,极大地降低了维护成本和维护时间
缺点:
1:降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。
2:有时会导致 级联 的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。
3:增加了开发成本。
二,Spring相关概念
1:轻量级的容器:
容器:spring容器帮我们管理业务逻辑层,里边有很多业务逻辑对象,有对象就有对象的生命周期的管理(创建,销毁).
轻量级:容器给予的业务逻辑对象多少种服务?spring给用户提供的服务完全有用户自己决定,spring想用什么服务自己开启使用。但是重量级的都是只要你用就把所有的服务都给你,不能自己定制。
spring容器从来不能独立运行,一定借助于其他容器启动,或者借助web容器启动。
spring容器 IOC容器: 管理对象的 创建 销毁 调用
特点:
应用模块之间耦合度小,组件都是可重用的,都是各自打包的
Spring 所有对象 都可以理解为组件
2:why spring?
1)动态解藕,方便开发,面向接口设计
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用
2)方便程序的测试TDD(Test-Driven Development)
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。
3)降低Java EE API的使用难度
Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个简单的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。
4)方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Mybatis)等的直接支持。
5)AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
6)声明式事务的支持:
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
7)对异常的处理方式,所有的都转换成Unchecked的。
8)它不是一个一体化的解决方案。
9)良好的设计,容易扩展,很多可重用的组件
=================================================
3,Spring核心组件(我们主要学习的是IOC和AOP模块)
(1)、Spring Core(IOC) 核心容器,提供组件的创建、装备、销毁
(2)、Spring Context Spring上下文,是一个接口ApplicationContext(继承自BeanFactory接口)的实现
(3)、Spring Web容器,web应用上下文,是webApplicationContext接口的实现
(4)、Spring DAO容器,是SpringDAO 支持模块,是为了简化DAO的使用
(5)、Spring ORM
(6)、Spring AOP ,对AOP编程支持的模块
(7)、Spring MVC,类似于Spring表示层web的一个框架
=================================================
4,spring ioc(must):
IOC:Inversion of Control 控制反转
一种说法:对象之间的依赖关系,由容器在运行时依据配置文件动态的建立
另一种说法:对象的控制器转移了,转到外部容器了,避免了代码的纠缠,代码更容易被维护,模板之间的耦合性降低,容易测试
IoC 控制反转意味着将你设计好的类交给容器去控制,而不是在类的内部进行控制,即控制权由应用代码中转到了外部容器
IoC:包括两部分内容
DI:Dependency Injection依赖注入,组件不做定位查询,只提供相应方法,由容器创建对象,并调用相应方法设置所需对象需要的组件(对象)
向springioc容器中的一个对象 注入 spring容器中另一个对象
(不要求)DL:Dependency Loopup依赖查找,容器创建对象并提供回调接口和上下文环境给组件,需要时通过接口从容器中查找对象
依赖查找,现在使用不太多。(EJB使用的更多,将对象创建好后,放到容器中。)
IOC解决:对象谁来创建的问题
DI解决:对象间的关系如何建立的问题。
org.springframework.beans及org.springframework.context包是IOC容器的基础
=================================================
5,SpringIOC核心api
BeanFactory接口和容器Map<String,Object>
BeanFactory是Spring中Bean容器,IoC的核心接口,主要用于处理Bean的初始化和配置,建立对象间的依赖关系
定义了如下方法:
从ioc容器中获取对象 Map<String,Object>
Object getBean(String name)//根据指定名称返回一个Bean实例
{ return map.get(name);
}
<T> T getBean(Class<T> requiredType) //返回一个与给定Class唯一匹配的Bean实例
<T> T getBean(String name, Class<T> requiredType)
Object getBean(String name, Object... args)
Class<?> getType(String name) //得到名称为name的Bean的Class对象
boolean isPrototype(String name) //判断名称为name的Bean是否是原型,
即是否总是返回一个新实例
boolean isSingleton(String name) //判断名称为name的Bean是否是单例
boolean containsBean(String name) //判断是否包含给定名称的Bean实例
boolean isTypeMatch(String name, Class<?> targetType)
//判断名称为name的Bean实例是否为targetType类型
String[] getAliases(String name) //如果名称为name的Bean有别名返回
通过getBean方法便可以得到相应的类实例,但是最好永远不调用,而使用注入,
避免对Spring API的依赖
在Spring中,同一Spring容器中的bean默认情况下是Singleton(单例),将在bean
的作用域介绍.
ApplicationContext接口
该接口继承于BeanFactory,增强了BeanFactory,提供了事务处理AOP,国际化,事件传递
所以在代码中我们一般会使用ApplicationContext接口,以及这个接口相应的实现类来创建spring的容器对象。
例如:
String path = "com/briup/ioc/set/set.xml";
ApplicationContext container = new ClassPathXmlApplicationContext(path);
//通过xml创建springIOC容器
ClassPathXmlApplicationContext:通过xml的路径产生ioc容器
=================================================
6,配置文件
Spring通过读取配置文件中的数据来对应用中各个对象进行实例化,配置以及组装,通常使用XML文件来作为配置文件。
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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
</beans>
这个文件的头部声明可以在下载的spring文档中的示例找到.注意这里面用到了俩个schema文件(.xsd文件),就是spring-beans-3.2.xsd和spring-context-3.2.xsd,这俩个文件在下载的spring文档中也是可以找到的.我们在Eclipse中把xml文件和schema文件关联(注意上课的演示步骤)后,在这个xml中就可以有智能提示了。还有一点需要注意,将来我们这xml中再引入另外的schema文件后在配置的时候就可以智能提示出新的配置内容,spring是又很多模块组成的,我们需要什么功能的配置标签就需要引入对应的schema文件,从而获得智能提示,同时也能帮我们验证配置的正确性
配置schema验证的提示:
window->preferences->Xml catalog->add->key(schame限定) and location(schame文件的源位置)-->key type选成 Schema Location --->ok完成
schame地址:http://www.springframework.org/schema/context/spring-context-3.2.xsd
=================================================
7,IOC注入
1)set方式注入(必须依靠set方法)
可以注入的内容有:
A、基本类型(8中基本类型+字符串)的装配
B、对象类型的装配
C、集合的装配
A、基本类型的装配
方式: 配置元素<value/>
例子:
public class HelloBean {
private String name;
private int age;
public String sayHello(){
return "hello "+name +",your age is" + age;
}
.............
}
Object o = Class.forName(ioc.HelloBean).newInstance();
method = class.method("setName",String.calss);
method.invoke(o,"tom")
配置文件set.xml
<bean id="helloBean" class="ioc.HelloBean">
<property name="name">
<value>tom</value>
</property>
<property name="age" value="20">
</property>
</bean>
<!--
id是Bean的唯一标识,要求在整个配置文件中要唯一,也可使用name属性,bean标签里面的id和name属性都可以用来标识这个配置的对象,但是id会帮我们检查给对象起的名字是否规范(名字不能重复、不能用数字开头、不能有空格等等),如果检查出来了那么就会报错。name属性不会帮检查这些东西。
-->
<!--property 对于所有用set方式来注入的必须改标签-->
<!--value 是对以基本类型,都用value(标签/属性)来注入,可以实现自动的数据类型转换-->
<!-ref 引用类型
测试类:
main:
ApplicationContext ac =
new ClassPathXmlApplicationContext("set.xml");
//获取容器的一个实例
HelloBean hb = (HelloBean) ac.getBean("helloBean");
System.out.println(hb.sayHello());
B、对象类型的装配
(1)、<ref local=" "/> 用于涉及的对象的id在当前配置文件中(用于在本配置文件中配置了的bean的引入同ref="..")
(2)、<ref bean=" "/> 用于涉及的对象的id不在本配置文件中
(用于引用不在本配置文件中配置的bean)
(3)、使用property的ref属性引用
public class OtherBean {
private String str1;
public String getStr1() {
return str1;
}
public void setStr1(String str1) {
this.str1 = str1;
}
public String toString(){
return "OtherBean "+str1;
}
}
public class SomeBean {
private OtherBean ob;
public void printInfo(){
System.out.println("someBean "+ob);
}
public OtherBean getOb() {
return ob;
}
public void setOb(OtherBean ob) {
this.ob = ob;
}
}
配置applicationContext.xml
<bean id="someBean" class="ioc.SomeBean">
<property name="ob" >
<ref bean="otherBean" />
</property>
</bean>
配置other.xml文件
<bean id="otherBean" class="ioc.OtherBean">
<property name="str1">
<value>string1</value>
</property>
</bean>
测试类:
main:
String[] path = {"ioc/applicationContext.xml","ioc/other.xml"};
ApplicationContext ac = new ClassPathXmlApplicationContext(path);
SomeBean sb = (SomeBean) ac.getBean("someBean");
sb.printInfo();
C、集合的装配
方式:配置元素<list> <set> <map> <props>
public class SomeBean {
private List listProperty;
private Set setProperty;
private Map mapProperty;
private Properties<String,String> property;
public List getListProperty() {
return listProperty;
}
public void setListProperty(List listProperty) {
this.listProperty = listProperty;
}
public Set getSetProperty() {
return setProperty;
}
public void setSetProperty(Set setProperty) {
this.setProperty = setProperty;
}
public Map getMapProperty() {
return mapProperty;
}
public void setMapProperty(Map mapProperty) {
this.mapProperty = mapProperty;
}
public Properties getProperty() {
return property;
}
public void setProperty(Properties property) {
this.property = property;
}
public void printInfo(){
System.out.println("listProperty");
System.out.println(listProperty);
System.out.println("setProperty");
System.out.println(setProperty);
Set set = mapProperty.entrySet();
Iterator it = set.iterator();
while(it.hasNext()){
Map.Entry entry = (Entry) it.next();
System.out.println("Key " +entry.getKey() );
System.out.println("value "+entry.getValue());
}
System.out.println("props: ");
Set set2 = property.entrySet();
Iterator it2 = set2.iterator();
while(it2.hasNext()){
Map.Entry entry= (Entry) it2.next();
System.out.println("key "+entry.getKey());
System.out.println("value "+entry.getValue());
}
}
}
applcationContext.xml的写法:
<bean id="someBean" class="ioc.SomeBean">
<property name="listProperty">
<list value-type="">
<value>list1</value>
<value>list1</value>
<value>list3</value>
</list>
</property>
<property name="setProperty">
<set>
<value>set1</value>
<value>set1</value>
<value>set3</value>
</set>
</property>
<property name="mapProperty">
<map>
<entry key="key1">
<value>value1</value>
</entry>
<entry key="key2">
<value>value2</value>
</entry>
</map>
</property>
<property name="property">
<props>
<prop key="key1">prop1</prop>
<prop key="key2">prop2</prop>
<prop key="key3">prop3</prop>
</props>
</property>
</bean>
测试类:
main:
String path = "ioc/applicationContext.xml";
ApplicationContext ac =
new ClassPathXmlApplicationContext(path);
SomeBean sb = (SomeBean) ac.getBean("someBean");
sb.printInfo();
<property/>
2)基于构造器注入 public Student(int a ,String name)
方式: 配置<constructor-arg>元素
在Bean中不用写set方法,但是要有相应的构造器
构造器注入有俩种形式 一个是根据参数类型 一个是根据参数位置的下标
<constructor-arg type="int" value="">
<constructor-arg index="0" value="">
例如:
构造器 参数 类型: 数据类型都不一样-->type
构造器 参数 类型: 有一样-->index 0
public S(int a ,ina b,int c){}
<bean name="student" class="com.briup.bean.Student">
<constructor-arg type="int" value="25">
</constructor-arg>
<constructor-arg type="java.lang.String" value="tom">
</constructor-arg>
<constructor-arg type="long" value="100">
</constructor-arg>
</bean>
或者:
<bean name="student" class="com.briup.bean.Student">
<constructor-arg index="2">
<value>30</value>
</constructor-arg>
<constructor-arg index="0">
<value>200</value>
</constructor-arg>
<constructor-arg index="1">
<value>lily</value>
</constructor-arg>
</bean>
3)自动注入 : 容器依照一些规则去装配bean中的一个属性
注意:自动装配只对[对象类型]起作用,对基本类型不起作用.
第一种情况:
在beans标签中配置装载方式
default-autowire="byName"
在根元素beans中加入这个属性,那么下面所有的bean都会
使用byName的方式进行自动注入,如果在下面的某一个bean
里面想使用其他的方式进行注入,可以用autowire=""属性进行
说明,或者某一个bean不想使用任何自动注入就使用autowire="no"
第二种情况:
在bean标签中指定配置方式
autowire="byName":
spring容器会到当前的类中找property的名字,然后
再根据这个名字去spring容器中找有没有和这个property
名字相同的对象,有的话,就把这个对象当做参数放到
setXxxx这个方法里面注入进来.
注意:了解property指的类中的什么东西。
autowire="byType":
spring容器会根据当前类中的set方法里面参数的类型,
去容器中找相匹配的对象,如果没找到就算了,如果找到
一个就注入进来,如果找到多个,那么就会报错了.
beans:
4)继承:并不是oo的继承关系
bean的定义的继承,指bean的配置可去继承
true 抽象化 代码中不能getBean获取其对象
abstract =
false 默认
parent = "父类bean的id/name"
例子:
<bean name="student" class="com.briup.bean.Student">
<property name="name">
<value>zhangsan</value>
</property>
</bean>
<!--
abstract="true" 表示当前的配置是一个抽象的配置,
这时候我们在代码中就不能通过这个bean的名字teacher来
获得相应的对象了(和java中的抽象类不能直接new对象的道理一样)
但是我们可以在写一个配置去继承这个抽象的配置,当然即使当前
这个配置不是抽象的,也能够被继承(和java中继承一样)
-->
<bean name="teacher" class="com.briup.bean.Teacher" abstract="true">
<property name="student" ref="student"></property>
</bean>
<!--
parent="teacher" 表示当前配置是继承了另外一个名字叫
teacher的bean的配置,配置和配置的继承像java中的类和类
直接的继承一样,子类会把父类中的对象继承过来.
当然在子配置里面依然是可以覆盖父配置中已经写的配置信息.
-->
<bean name="t" parent="teacher">
<property name="id">
<value>11</value>
</property>
<property name="name">
<value>TeacherWang</value>
</property>
</bean>
==========================================
8,bean对象的生命周期
生命周期执行的过程如下:
1)spring对bean进行实例化,默认bean是单例
2)spring对bean进行依赖注入
3)如果bean实现了BeanNameAware接口,spring将bean的id传给setBeanName()方法
4)如果bean实现了BeanFactoryAware接口,spring将调用setBeanFactory方法,将BeanFactory实例传进来
5)如果bean实现了ApplicationContextAware()接口,spring将调用setApplicationContext()方法将应用上下文的引用传入
6)如果bean实现了BeanPostProcessor接口,spring将调用它们的postProcessBeforeInitialization接口方法
7)如果bean实现了InitializingBean接口,spring将调用它们的afterPropertiesSet接口方法,类似的如果bean使用了init-method="类中方法"属性声明了初始化方法,改方法也会被调用
8)如果bean实现了BeanPostProcessor接口,spring将调用它们的postProcessAfterInitialization接口方法
9)此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁
10)若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用
其实很多时候我们并不会真的去实现上面说描述的那些接口,那么下面我们就除去那些接口针对bean的单例和非单例来描述下bean的生命周期:
单例管理的对象:
1.默认情况下,spring在读取xml文件的时候,就会创建对象。
2.在创建的对象的时候(先调用构造器),会去调用init-method=".."
属性值中所指定的方法.
3.对象在被销毁的时候,会调用destroy-method="..."属性值中
所指定的方法.(例如调用container.destroy()方法的时候)
4.lazy-init="true",可以让这个对象在第一次被访问的时候创建
单例懒加载:ioc容器创建的时候 不创建对象
在第一次访问的时候创建对象
容器销毁的时候销毁
创建 无参构造器
初始化 调用方法(我指定)
...
销毁 调用方法(我指定)
非单例管理的对象:
1.spring读取xml文件的时候,不会创建对象.
2.在每一次访问这个对象的时候,spring容器都会创建这个对象,并且
调用init-method=".."属性值中所指定的方法.
3.对象销毁的时候,spring容器不会帮我们调用任何方法,
因为是非单例,这个类型的对象有很多个,spring容器一旦把
这个对象交给你之后,就不再管理这个对象了.
1 xml文件导入其他xml文件配置
如果我们在spring框架中配置了多个xml文件,我们可以在读取配置文件的时候把这些xml文件一下全都读取,也可以只读一个总的xml文件,在这个总的xml文件中把其他的xml全都都导入进来。
例如:
student.xml文件:
<bean name="student" class="com.briup.bean.Student">
<property name="id">
<value>25</value>
</property>
</bean>
teacher.xml文件:
<bean name="teacher" class="com.briup.bean.Teacher">
<property name="student" ref="student"></property>//ref=""手动注入哪个bean
</bean>
import.xml文件:
<import resource="teacher.xml"/>
<import resource="student.xml"/>
通过多个xml创建一个ioc容器
main:
String[] path = {"com/briup/ioc/imp/import.xml"};
ApplicationContext container = new ClassPathXmlApplicationContext(path);
Teacher t = (Teacher) container.getBean("teacher");
System.out.println(t.getStudent());
2 创建bean实例的方式
1) xml文件中有bean的配置,而且这个bean所对应的java类中存在一个无参构造器,那么这个时候spring容器就可以使用反射调用无参构造器来创建实例了
2) 通过工厂类获得实例(工厂类实现了接口FactoryBean<?>)
动态配置参数:PropertyPlaceholderConfigurer类的使用
注意spring中的PropertyPlaceholderConfigurer类的使用,在htmlsingle中直接搜索类名即可
例如:
工厂类实现指定接口并且实现接口中的三个抽象方法:
public class ConnectionFactory implements FactoryBean<Connection>{
private String driver;
private String url;
private String username;
private String password;
@Override
public Connection getObject() throws Exception {
Class.forName(driver);
Connection conn =
DriverManager.getConnection(url,username,password);
return conn;
}
@Override
public boolean isSingleton() {//判断它是否是单例
// TODO Auto-generated method stub
return false;
}
@Override
public Class<!-- <Connection> --> getObjectType() {
// TODO Auto-generated method stub
return Connection.class;
}
set/get
....
}
xml文件:
<!--
因为这个类是一个工厂类,所以我们用名字conn在容器中拿对象的时候,
拿到并不是这个工厂类对象,而是这个工厂类对象调用完工厂方法后所返回的对象.
-->
<bean name="conn" class="com.briup.ioc.factory.ConnectionFactory">
<property name="driver">
<value>${driver}</value>//从一个配置文件中以key—value的形式拿value
</property>
<property name="url">
<value>${url}</value>
</property>
<property name="username">
<value>${username}</value>
</property>
<property name="password">
<value>${password}</value>
</property>
</bean>
<!--
下面配置的这个类,可以自动的帮我们去读取指定的properties文件的
内容,文件中用key-value的形式存放数据,读完之后我们就可以用
${key}这种形式去拿文件中的value值了。
classpath指的是从src下面找.
-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:oracle.properties</value>
</property>
</bean>
main:
String path = "com/briup/ioc/factory/factory.xml";
ApplicationContext container =
new ClassPathXmlApplicationContext(path);
Connection conn = (Connection) container.getBean("conn");
System.out.println(conn);
3) 通过实例工厂获得实例(不需要实现或者继承任何接口或者父类)
分两步:
1,先把工厂配置成Spring 的 bean
2,再配置一个工厂 把哪个bean作为工厂 把bean中的某个方法作为产生实例的方法
<bean factory-bean="" factory-method="">
注意spring中的PropertyPlaceholderConfigurer类的使用,在htmlsingle中直接搜索类名即可
例如:一个普通的工程类
public class ConnectionFactory{
private String driver;
private String url;
private String username;
private String password;
public Object getConnection() throws Exception {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url,username,password);
return conn;
}
get/set
....
}
xml文件:
<bean name="factory_bean" class="com.briup.ioc.instanceFactory.ConnectionFactory">
<property name="driver">
<value>${driver}</value>
</property>
<property name="url">
<value>${url}</value>
</property>
<property name="username">
<value>${username}</value>
</property>
<property name="password">
<value>${password}</value>
</property>
</bean>
<!--
将来通过这个conn来拿对象,拿到的是名字为factory的工厂类调用完
名字为getConnection方法之后所返回的对象。
-->
<bean name="conn" factory-bean="factory_bean" factory-method="getConnection"></bean>
<!-- 读取properties文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:oracle.properties</value>
</property>
</bean>
main:
String path = "com/briup/ioc/instanceFactory/instanceFactory.xml";
ApplicationContext container =
new ClassPathXmlApplicationContext(path);
Connection conn = (Connection) container.getBean("conn");
System.out.println(conn);
4) 通过静态工厂获得实例
例如:含义静态方法的工厂类
public class ConnectionFactory{
private static String driver = "oracle.jdbc.driver.OracleDriver";
private static String url = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
private static String username = "briup";
private static String password = "briup";
public static Object getConnection() throws Exception {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url,username,password);
return conn;
}
}
xml文件:
<!-- 这样配置一定要求getConnection方法是静态方法 -->
<bean name="conn" class="com.briup.ioc.staticFactory.ConnectionFactory" factory-method="getConnection"></bean>
main:
String path = "com/briup/ioc/staticFactory/staticFactory.xml";
ApplicationContext container = new ClassPathXmlApplicationContext(path);
Connection conn = (Connection) container.getBean("conn");
System.out.println(conn);
3 自定义属性编辑器 PropertyEditor
可以使用自定义属性编辑器来处理:
当需要一个对象的时候 却给了一个String
string -> student
Spring中我们可以使用属性编辑器来将特定的字符串转换为对象 :String-->object : 怎么转化
java.beans.PropertyEditor(JDK中的接口)用于将xml文件中字符串转换为特定的类型
同时JDK为我们提供一个实现类java.beans.PropertyEditorSupport
Spring在注入时,如果遇到类型不一致(例如需要Address类型但是用户传了个String)则会去调用相应的属性编辑器进行转换.
spring会调用属性编辑器的setAsText(String str)进行处理用户传的字符串,并调用getValue()方法获取处理后得到的对象,所以我们在代码中处理完后记得调用setValue方法,要不然spring调用getValue方法拿不到你处理后的对象
自定义属性编辑器示例:
配置自定义属性编辑器:注意spring中的CustomEditorConfigurer类的使用,在htmlsingle中直接搜索类名即可
//自定义编辑器类
public class AddressEditor extends PropertyEditorSupport {
@Override
public String getAsText() {
return super.getAsText();
}
//Spring遇到数据类型不一致并且不能自己处理的时候会调用这个方法处理字符串
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] str = text.split(",");
String city = str[0];
String street = str[1];
String country = str[2];
Address add = new Address(city, street, country);
setValue(add);
}
}
//Address类
public class Address {
private String city;
private String street;
private String country;
set/get
.....
}
//Student类
public class Student {
private long id;
private String name;
private boolean gender;
private int age;
private Address address;
get/set
...
}
xml文件:
<!-- 这个配置指明哪个类型对应哪个自定义编辑器 -->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="com.briup.ioc.proEdit.Address" value="com.briup.ioc.proEdit.AddressEditor"/>
</map>
</property>
</bean>
<!-- spring发现address的值不能注入的时候(类型不对),就会调用对应的属性编辑器处理了 -->
<bean id="student" class="com.briup.ioc.proEdit.Student">
<property name="id" value="1"/>
<property name="name" value="tom"/>
<property name="age" value="45"/>
<property name="gender" value="true"/>
<property name="address">
<value>kunshan,xueyuan,China</value>
</property>
</bean>
4 自定义事件 IOC
:事件
:自定义事件 一个类变成自定义事件类 继承ApplicationEvent
:发布事件
:监听 一个类变成监听类 实现ApplicationListener
在spring中我们可以自定义事件,并且可以使用ApplicationContext类型对象(就是spring容器container)来发布这个事件, 事件发布 之后,所有的ApplicaitonListener(监听器)实例都会被触发并调用指定方法onApplicationEvent()来处理.
例如:
自定义事件类RainEvent:
public class RainEvent extends ApplicationEvent {
public RainEvent(Object source) {
super(source);
}
}
监听器类RainListener1
public class RainListener1 implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof RainEvent) {
System.out.println("不听课的学生发现 : " + event.getSource() + " 要准备睡觉了...");
}
}
}
监听器类RainListener2
public class RainListener2 implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof RainEvent) {
System.out.println("老师 : " + event.getSource() + " 现在各位休息一下");
}
}
}
xml文件:
<!-- 只需要把这俩个监听器类交给spring容器管理就可以了 -->
<bean class="com.briup.ioc.event.RainListener1"></bean>
<bean class="com.briup.ioc.event.RainListener2"></bean>
main:
String path = "com/briup/ioc/event/event.xml";
ApplicationContext container = new ClassPathXmlApplicationContext(path);
container.publishEvent(new RainEvent("下课了!"));
5 ioc中的annotation配置
重点:
@Autowired 有一个载体
1 @Autowired默认按照byType匹配的方式进行注入,如果没有一个bean的类型是匹配的则会抛异常,
如果有多个bean的类型都匹配成功了,那么再按byName方式进行选择
2 @Autowired注解可以写在成员变量、set方法、构造器函数上面
3 @Autowired如果最终匹配不成功(注意一定是一个都没有找到的情况,找到多个还是会报错)则会抛出异常,
但是如果设置为@Autowired(required=false),则最终匹配不成功(一个都没有找到)没有不会抛出异常。
4 @Autowired可以结合 @Qualifier("beanName")来使用,则可以达到byName的效果
5 @Autowired使用后需要在xml文件加入以下配置才能生效:
<context:annotation-config/>
@Resource
1 @Resource的作用和 @Autowired差不多,只不过@Resource是默认先用byname,如果找不到合适的就再用bytype来注入
2 @Resource有俩个属性,name和type,使用name属性则表示要byName匹配,使用type属性则表示要byType匹配
3 @Resource使用后需要在xml文件加入以下配置才能生效:
<context:annotation-config/>
@PostConstruct 和 @PreDestroy
写在方法上
1 标注了 @PostConstruct 注解的方法将在类实例化后调用。
2 标注了 @PreDestroy 的方法将在类销毁之前调用。
1 @Component注解可以直接定义bean,而无需在xml定义。但是若两种定义同时存在,xml中的定义会覆盖类中注解的Bean定义。
2 @Component注解直接写在类上面即可
3 @Component有一个可选的参数,用于指定 bean 的名称:@Component("boss_1") <bean name="boss_1">
4 @Component容易不指定参数,则 bean 的名称为当前类的类名小写
5 @Component使用之后需要在xml文件配置一个标签: <context:component-scan base-package=""/>
6 <context:component-scan base-package="com.briup.ioc.annotation" /> 可以表示spring需要检查哪个包下的java类,看它们是否使用了 @Component注解
7 @Component定义的bean默认情况下都是单例模式的,如果要让这个bean变为非单例,可以再结合这个 @Scope 注解来达到目标 @Scope("prototype")
@Component是Spring中所有bean组件的通用形式, @Repository @Service @Controller 则是 @Component的细化,用来表示更具体的用例,分别对应了持久化层、服务层和表现层。但是至少到现在为止这个四种注解的实质区别很小(甚至几乎没有),都是把当前类注册为spring容器中的一个bean
@Component
三大框架结合的时候 最好这样配置 不然要报错
三层架构中:dao-->@Repository
service-->@Service
web-->@Controller
注意:
component-scan标签默认情况下自动扫描指定路径下的包(含所有子包),将带有 @Component @Repository @Service @Controller标签的类自动注册到spring容器。对标记了 Spring中的 @Required @Autowired @PostConstruct @PreDestroy @Resource @WebServiceRef @EJB @PersistenceContext @PersistenceUnit等注解的类进行对应的操作使注解生效(包含了annotation-config标签的作用)。
spring :
web : 表现层 :servlet+jsp
service : 业务逻辑层 :java
dao : 数据访问层 :jdbc、mybatis
spring : 作用在项目的任何一个层面
但是代码不多
(以xml配置的形式表现功能)以注解表现功能
spring :
IOC容器 : 控制反转 : 动态解耦
代码和代码的耦合度 : 代码中使用了其他的类对象
//service 业务逻辑层
ICate{
public void findAll();
}
CateService im ICate{
public void findAll(){
}
}
Cate222222 im ICate{
public void findAll(){
}
}
//web层代码
@WebServlet("/login")
CateServlet{
doget(){
ICate c = map.get("cate");
c.findAll();
}
}
static Map<String,Object> map ;
AOP : 面向【切面】编程
很多点(方法)-->面
具有相同特点的 (方法)
统一的编程操作
【简化编程】
IOC 核心API :
BeanFacotry 接口
ApplicationContext 接口
ClassPathXmlApplicationContext 类
使用xml构建ioc容器
AnnotationConfigApplicationContext 类
使用注解构建ioc容器
对象放到ioc容器中 : 在xml中使用Bean标签
给ioc容器中的bean【设置】值 : 调用set方法 设置值
<propety name="prop" value/ref>//ref 值为引用类型时用
给ioc容器中的bean【设置】值 :调用构造器 设置值
默认单例 : 修改为非单例<bean score="prototype">
非单例对象 : 使用同一个名字获取多次 该对象 ,每次获取的是不同的对象
自动注入 : 注入引用数据类型 ,
从当前ioc容器中获得一个对象
注入 到当前ioc容器的另一个bean对象中
生命周期 :
1,创建 : 执行无参构造器
2,初始化 : 执行预定义代码
3,执行 : 从容器中获取出来使用
4,销毁 : 执行预定义代码
1,创建 : 在容器创建的时候创建 所有单例对象
2,初始化 : 创建完立刻初始化 init-method='方法名'
4,销毁 : 当容器销毁的时候销毁
1,创建 : 非单例对象 : 获取的时候创建
2,初始化 : 创建完立刻初始化 init-method='方法名'
4,销毁 : 容器不管理
2 创建bean实例的方式
1) xml文件中有bean的配置,而且这个bean所对应的java类中存在一个无参构造器,
那么这个时候spring容器就可以使用反射调用无参构造器来创建实例了
2) 通过工厂类获得实例(工厂类实现了接口FactoryBean<?>)
动态配置参数:PropertyPlaceholderConfigurer类的使用
注意spring中的PropertyPlaceholderConfigurer类的使用,在htmlsingle中直接搜索类名即可
例如:
工厂类实现指定接口并且实现接口中的三个抽象方法:
public class ConnectionFactory implements FactoryBean<Connection>{
private String driver;
private String url;
private String username;
private String password;
@Override
public Connection getObject() throws Exception {
Class.forName(driver);
Connection conn =
DriverManager.getConnection(url,username,password);
return conn;
}
@Override
public boolean isSingleton() {//判断它是否是单例
// TODO Auto-generated method stub
return false;
}
@Override
public Class<!-- <Connection> --> getObjectType() {
// TODO Auto-generated method stub
return Connection.class;
}
set/get
....
}
xml文件:
<!--
因为这个类是一个工厂类,所以我们用名字conn在容器中拿对象的时候,
拿到并不是这个工厂类对象,而是这个工厂类对象调用完工厂方法后所返回的对象.
-->
<bean name="conn" class="com.briup.ioc.factory.ConnectionFactory">
<property name="driver">
<value>${driver}</value>//从一个配置文件中以key—value的形式拿value
</property>
<property name="url">
<value>${url}</value>
</property>
<property name="username">
<value>${username}</value>
</property>
<property name="password">
<value>${password}</value>
</property>
</bean>
<!--
下面配置的这个类,可以自动的帮我们去读取指定的properties文件的
内容,文件中用key-value的形式存放数据,读完之后我们就可以用
${key}这种形式去拿文件中的value值了。
classpath指的是从src下面找.
-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:oracle.properties</value>
</property>
</bean>
main:
String path = "com/briup/ioc/factory/factory.xml";
ApplicationContext container =
new ClassPathXmlApplicationContext(path);
Connection conn = (Connection) container.getBean("conn");
System.out.println(conn);
3) 通过实例工厂获得实例(不需要实现或者继承任何接口或者父类)
分两步:
1,先把工厂配置成Spring 的 bean
2,再配置一个工厂 把哪个bean作为工厂 把bean中的某个方法作为产生实例的方法
<bean factory-bean="" factory-method="">
注意spring中的PropertyPlaceholderConfigurer类的使用,在htmlsingle中直接搜索类名即可
例如:一个普通的工程类
public class ConnectionFactory{
private String driver;
private String url;
private String username;
private String password;
public Object getConnection() throws Exception {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url,username,password);
return conn;
}
get/set
....
}
xml文件:
<bean name="factory_bean" class="com.briup.ioc.instanceFactory.ConnectionFactory">
<property name="driver">
<value>${driver}</value>
</property>
<property name="url">
<value>${url}</value>
</property>
<property name="username">
<value>${username}</value>
</property>
<property name="password">
<value>${password}</value>
</property>
</bean>
<!--
将来通过这个conn来拿对象,拿到的是名字为factory的工厂类调用完
名字为getConnection方法之后所返回的对象。
-->
<bean name="conn" factory-bean="factory_bean" factory-method="getConnection"></bean>
<!-- 读取properties文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:oracle.properties</value>
</property>
</bean>
main:
String path = "com/briup/ioc/instanceFactory/instanceFactory.xml";
ApplicationContext container =
new ClassPathXmlApplicationContext(path);
Connection conn = (Connection) container.getBean("conn");
System.out.println(conn);
4) 通过静态工厂获得实例
例如:含义静态方法的工厂类
public class ConnectionFactory{
private static String driver = "oracle.jdbc.driver.OracleDriver";
private static String url = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
private static String username = "briup";
private static String password = "briup";
public static Object getConnection() throws Exception {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url,username,password);
return conn;
}
}
xml文件:
<!-- 这样配置一定要求getConnection方法是静态方法 -->
<bean name="conn" class="com.briup.ioc.staticFactory.ConnectionFactory" factory-method="getConnection"></bean>
main:
String path = "com/briup/ioc/staticFactory/staticFactory.xml";
ApplicationContext container = new ClassPathXmlApplicationContext(path);
Connection conn = (Connection) container.getBean("conn");
System.out.println(conn);
3 自定义属性编辑器 PropertyEditor
可以使用自定义属性编辑器来处理:
当需要一个对象的时候 却给了一个String
string -> student
Spring中我们可以使用属性编辑器来将特定的字符串转换为对象 :String-->object : 怎么转化
java.beans.PropertyEditor(JDK中的接口)用于将xml文件中字符串转换为特定的类型
同时JDK为我们提供一个实现类java.beans.PropertyEditorSupport
Spring在注入时,如果遇到类型不一致(例如需要Address类型但是用户传了个String)则会去调用相应的属性编辑器进行转换.
spring会调用属性编辑器的setAsText(String str)进行处理用户传的字符串,并调用getValue()方法获取处理后得到的对象,所以我们在代码中处理完后记得调用setValue方法,要不然spring调用getValue方法拿不到你处理后的对象
自定义属性编辑器示例:
配置自定义属性编辑器:注意spring中的CustomEditorConfigurer类的使用,在htmlsingle中直接搜索类名即可
//自定义编辑器类
public class AddressEditor extends PropertyEditorSupport {
@Override
public String getAsText() {
return super.getAsText();
}
//Spring遇到数据类型不一致并且不能自己处理的时候会调用这个方法处理字符串
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] str = text.split(",");
String city = str[0];
String street = str[1];
String country = str[2];
Address add = new Address(city, street, country);
setValue(add);
}
}
//Address类
public class Address {
private String city;
private String street;
private String country;
set/get
.....
}
//Student类
public class Student {
private long id;
private String name;
private boolean gender;
private int age;
private Address address;
get/set
...
}
xml文件:
<!-- 这个配置指明哪个类型对应哪个自定义编辑器 -->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="com.briup.ioc.proEdit.Address" value="com.briup.ioc.proEdit.AddressEditor"/>
</map>
</property>
</bean>
<!-- spring发现address的值不能注入的时候(类型不对),就会调用对应的属性编辑器处理了 -->
<bean id="student" class="com.briup.ioc.proEdit.Student">
<property name="id" value="1"/>
<property name="name" value="tom"/>
<property name="age" value="45"/>
<property name="gender" value="true"/>
<property name="address">
<value>kunshan,xueyuan,China</value>
</property>
</bean>
4 自定义事件 IOC
:事件
:自定义事件 一个类变成自定义事件类 继承ApplicationEvent
:发布事件
:监听 一个类变成监听类 实现ApplicationListener
在spring中我们可以自定义事件,并且可以使用ApplicationContext类型对象(就是spring容器container)来发布这个事件, 事件发布 之后,所有的ApplicaitonListener(监听器)实例都会被触发并调用指定方法onApplicationEvent()来处理.
例如:
自定义事件类RainEvent:
public class RainEvent extends ApplicationEvent {
public RainEvent(Object source) {
super(source);
}
}
监听器类RainListener1
public class RainListener1 implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof RainEvent) {
System.out.println("不听课的学生发现 : " + event.getSource() + " 要准备睡觉了...");
}
}
}
监听器类RainListener2
public class RainListener2 implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof RainEvent) {
System.out.println("老师 : " + event.getSource() + " 现在各位休息一下");
}
}
}
xml文件:
<!-- 只需要把这俩个监听器类交给spring容器管理就可以了 -->
<bean class="com.briup.ioc.event.RainListener1"></bean>
<bean class="com.briup.ioc.event.RainListener2"></bean>
main:
String path = "com/briup/ioc/event/event.xml";
ApplicationContext container = new ClassPathXmlApplicationContext(path);
container.publishEvent(new RainEvent("下课了!"));
5 ioc中的annotation配置
重点:
@Autowired 有一个载体
1 @Autowired默认按照byType匹配的方式进行注入,如果没有一个bean的类型是匹配的则会抛异常,
如果有多个bean的类型都匹配成功了,那么再按byName方式进行选择
2 @Autowired注解可以写在成员变量、set方法、构造器函数上面
3 @Autowired如果最终匹配不成功(注意一定是一个都没有找到的情况,找到多个还是会报错)则会抛出异常,
但是如果设置为@Autowired(required=false),则最终匹配不成功(一个都没有找到)没有不会抛出异常。
4 @Autowired可以结合 @Qualifier("beanName")来使用,则可以达到byName的效果
5 @Autowired使用后需要在xml文件加入以下配置才能生效:
<context:annotation-config/>
@Resource
1 @Resource的作用和 @Autowired差不多,只不过@Resource是默认先用byname,如果找不到合适的就再用bytype来注入
2 @Resource有俩个属性,name和type,使用name属性则表示要byName匹配,使用type属性则表示要byType匹配
3 @Resource使用后需要在xml文件加入以下配置才能生效:
<context:annotation-config/>
@PostConstruct 和 @PreDestroy
写在方法上
1 标注了 @PostConstruct 注解的方法将在类实例化后调用。
2 标注了 @PreDestroy 的方法将在类销毁之前调用。
1 @Component注解可以直接定义bean,而无需在xml定义。但是若两种定义同时存在,xml中的定义会覆盖类中注解的Bean定义。
2 @Component注解直接写在类上面即可
3 @Component有一个可选的参数,用于指定 bean 的名称:@Component("boss_1") <bean name="boss_1">
4 @Component容易不指定参数,则 bean 的名称为当前类的类名小写
5 @Component使用之后需要在xml文件配置一个标签: <context:component-scan base-package=""/>
6 <context:component-scan base-package="com.briup.ioc.annotation" /> 可以表示spring需要检查哪个包下的java类,看它们是否使用了 @Component注解
7 @Component定义的bean默认情况下都是单例模式的,如果要让这个bean变为非单例,可以再结合这个 @Scope 注解来达到目标 @Scope("prototype")
@Component是Spring中所有bean组件的通用形式, @Repository @Service @Controller 则是 @Component的细化,用来表示更具体的用例,分别对应了持久化层、服务层和表现层。但是至少到现在为止这个四种注解的实质区别很小(甚至几乎没有),都是把当前类注册为spring容器中的一个bean
@Component
三大框架结合的时候 最好这样配置 不然要报错
三层架构中:dao-->@Repository
service-->@Service
web-->@Controller
注意:
component-scan标签默认情况下自动扫描指定路径下的包(含所有子包),将带有 @Component @Repository @Service @Controller标签的类自动注册到spring容器。对标记了 Spring中的 @Required @Autowired @PostConstruct @PreDestroy @Resource @WebServiceRef @EJB @PersistenceContext @PersistenceUnit等注解的类进行对应的操作使注解生效(包含了annotation-config标签的作用)。
接下来就是spring另一个重点AOP
AOP的核心就是代理,所以接下来先了解什么代理
代理:
委托类(目标类): 目标对象 target
代理类: 代理对象 proxy
第三天:
1 代理模式
代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
注意:
委托类对象就是我们后面说到的--->目标对象(需要【被】代理的对象)
代理类对象就是我们后面说到的--->代理对象(目标对象就是需要这个对象做为代理)
按照代理类的创建时期,代理类可分为两种。
静态代理类:
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理类 :在程序运行时,运用反射机制动态创建而成。
2 静态代理
例如:
接口:HelloService
委托类:HelloServiceImpl
代理类:HelloServiceProxy
public interface HelloService{
public String echo(String msg);
public Date getTime();
}
public class HelloServiceImpl implements HelloService{
public String echo(String msg){
return "echo:"+msg;
}
public Date getTime(){
return new Date();
}
}
public class HelloServiceProxy implements HelloService{
private HelloService helloService; //表示被代理的HelloService 实例
public HelloServiceProxy(HelloService helloService){
this.helloService=helloService;
}
public void setHelloServiceProxy(HelloService helloService){
this.helloService=helloService;
}
public String echo(String msg){
System.out.println("before calling echo()"); //目标方法调前处理
//调用委托类对象的方法(也就是目标对象方法/被代理对象方法)
//这个方法才是我们真正要执行的方法
String result=helloService.echo(msg);
System.out.println("after calling echo()"); //目标方法调用后处理
return result;
}
public Date getTime(){
System.out.println("before calling getTime()"); //目标方法调前处理
//调用委托类对象的方法(也就是目标对象方法/被代理对象方法)
//这个方法才是我们真正要执行的方法
Date date=helloService.getTime();
System.out.println("after calling getTime()"); //目标方法调用后处理
return date;
}
}
main:
HelloService helloService=new HelloServiceImpl();
HelloService helloServiceProxy=new HelloServiceProxy(helloService);
System.out.println(helloServiceProxy.echo("hello"));
3 动态代理
jdk动态代理 :基于接口
必须要目标类实现了某个接口
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包下面的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。
例子:
接口:
public interface IStudentService {
void save(Student s);
void delete(long id);
Student find(long id);
}
日志类:
public class StudentLogger {
public void log(String msg){
System.out.println("log: "+msg);
}
}
实现类
public class StudentServiceImpl implements IStudentService {
public void delete(long id) {
// 记录日志
System.out.println("student is deleted...");
}
public Student find(long id) {
// 记录日志
System.out.println("student is found...");
return null;
}
public void save(Student s) {
// 记录日志
System.out.println("student is saved...");
}
}
//InvocationHandler接口的实现类,java的动态代理中需要使用
public class MyHandler implements InvocationHandler {
//目标对象
private Object target;
private StudentLogger logger = new StudentLogger();
public MyHandler() {
}
public MyHandler(Object target) {
this.target = target;
}
// 参数1 将来所产生的代理对象 Proxy4$
// 参数2 将来需要调用到的目标对象里面真正的那个方法的镜像
// 参数3 将来调用方法的时候所传的参数
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
// 获得将来所调用方法的名字
String methodName = m.getName();
// 用日志记录输出一下
logger.log(methodName + " is invoked...");
// 用反射的方式去调用将来需要真正调用的方法.
Object o = m.invoke(target, args);
return o;
}
get/set
....
}
main:
//目标对象
IStudentService service = new StudentServiceImpl();
//service是我们的目标对象。
//我们要给目标对象产生代理对象。
//目标对象service只能单独执行delete方法。
//但是我们需要的是:先执行log日志方法再执行delete方法。
//目标对象service做不到这个要求,所以我们要给目标对象service
//生成一个代理对象去完成这俩个操作.
//怎么给目标对象生成代理对象:
//JDK动态代理的方式
//获得目标对象的Class对象
Class c = service.getClass();
//获得目标对象的类加载器对象
ClassLoader classLoader = c.getClassLoader();
//获得目标对象所实现的所有接口
Class[] interfaces = c.getInterfaces();
//获得一个InvocationHandler接口的实现类对象,并把目标对象传进去
InvocationHandler h = new MyHandler(service);
//参数1 目标对象的类加载器对象
//参数2 目标对象所实现的所有接口. Class类型数组
//参数3 InvocationHandler接口的实现类对象
IStudentService proxy =(IStudentService)Proxy.newProxyInstance(classLoader, interfaces, h);
//这里的proxy是一个实现了IStudentService接口动态生成的代理类的对象
proxy.delete();
4 CGLib代理 通过拦截器实现 目标类不要实现接口
一个外部的jar包,但是spring把这个CGlib整合到了spring中,普通的没有实现任何接口的类 就可以使用Cglib动态代理.
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为目标对象创建一个子类对象,并在子类对象中拦截所有父类方法的调用,然后在方法调用前后调用后都可以加入自己想要执行的代码。JDK动态代理与CGLib动态代理都是Spring AOP的采用的代理方式。
简单的实现:
这是一个需要被代理的类,也就是父类,通过字节码技术创建这个类的子类,实现动态代理。
public class SayHello {
public void say(){
System.out.println("hello everyone");
}
}
注意:在cglib方式中,目标对象作为父类,代理对象作为目标对象动态生成的子类对象
该类实现了创建一个类的子类的方法(cglib给一个类生成代理对象的方式)
getProxy(SuperClass.class)方法通过参数即父类的class对象,创建出它的一个子类对象,也就是cglib方式的代理对象
intercept(Object obj, Method method, Object[] args,MethodProxy proxy)方法拦截所有目标类方法的调用,
obj表示将来生成的代理对象,
method为目标类中方法的反射对象,args为方法的动态入参,
mproxy为代理类(子类)中方法的反射对象。
mproxy.invokeSuper(obj, args)通过代理类调用目标对象(父类)中的方法。
public class CglibProxy implements MethodInterceptor{
public Object getProxy(Class clazz){
Enhancer enhancer = new Enhancer();
//设置谁是父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
//实现MethodInterceptor接口方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy mproxy) throws Throwable {
System.out.println("前置代理");
//通过代理类调用父类中的方法
Object result = mproxy.invokeSuper(obj, args);
System.out.println("后置代理");
return result;
}
}
main:
CglibProxy proxy = new CglibProxy();
//通过生成子类的方式创建代理类
SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);
proxyImp.say();
输出结果:
前置代理
hello everyone
后置代理
6 Spring实现AOP(Aspect Oriented Programming)是依赖JDK动态代理和CGLIB代理(不同情况spring会自己选择一种方式)。
JDK动态代理和CGLIB代理的对比:
JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的。
所以spring会有以下俩种选择动态代理实现方式的情况:
*如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
*如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间自动选择;
7 认识AOP中的一些基本概念,然后在一个一个的例子中,不断的加强对这些概念的理解,同时要能自己表述出每个概念的含义
AOP 面向切面编程
目标方法 目标类中需要被执行的方法
aspect 切面/切面类
封装方法,需要通知带到连接点/切入点 前/后执行
joinPoint 连接点
在spring的aop中只有 类中的方法 可以做连接点,每一个方法都可以是一个连接点.
pointCut 切入点 增强器
一组连接点的集合
<aop:pointCut>
advice 通知/拦截器 : 携带切面 到目标方法执行前后
用来控制切面类将来到底是织入到切入点的前面、后面或者是抛异常的时候。
adivsor 增强器/选择器 : 把连接点 变成切入点
用来筛选类中的哪些方法是我们的连接点(哪些方法需要被拦截).
target 目标对象
proxy 代理对象
wave 织入 :
advice分五种:
把切面类织入到切入点(一堆连接点)
1,前置通知(Before advice):
实现接口MethodBeforeAdvice
在某连接点(join point)之前执行的通知
2,返回后通知(After returning advice):
实现接口AfterReturningAdvice
在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
3,抛出异常后通知(After throwing advice):
实现接口ThrowsAdvice
在方法抛出异常退出时执行的通知。
4,后置通知(After (finally) advice):
实现接口AfterAdvice
当某连接点退出的时候执行的通知
5,环绕通知(Around Advice):
实现接口MethodInterceptor
包围一个连接点(join point)的通知,例如事务的处理,就需要这样的通知,因为事务需要在方法前开启,在方法后提交
面向切面编程AOP步骤:
1,准备目标类 : AccountServiceImpl
2,准备切面类 : MyAscept
3,准备advice通知 : BeforeAdvice 让一个类成为通知/拦截器 那么这个类需要实现接口MethodBeforeAdvice | ...
4,准备Spring代理类 : Spring自带 : ProxyFactoryBean (怎么使用查帮助文档)
5,准备配置文件xml :
1,配置切面类
2,配置通知
3,配置目标对象,以及目标对象需要依赖的对象
4,配置代理对象
测试 就获取代理对象,使用代理对象去执行目标类中的方法,查看是否成功代理
7 在Spring中,Advice是由spring中的几个接口来指定,主要有以下几种:
1) Before Advice 在目标方法执行前被 切面类 织入
普通的类变成前置通知:实现MethodBeforeAdvice接口
MethodBeforeAdvice接口:自定义类实现了这个接口 spring就会认为你是前置通知
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
例如:
//有一下几个类或者接口:
Account.java
private int id;
private String name;
private double balance;//余额
get/set
...
AccountDao.java
//取款 账号减去多少钱
void withdraw(Account acc,double amt);
//存款 账号加上多少钱
void deposit(Account acc,double amt);
AccountDaoImpl.java
//简单的实现接口中的抽象方式
IAccountService.java
//银行账号的一个操作:例如转账
void bankAction();
AccountServiceImpl.java
private AccountDao accountDao;
private Account account;
//转账
public void bankAction(){
accountDao.withdraw(account, 100);
accountDao.deposit(account, 100);
}
get/set
...
//切面类
public class MyLogger {
public void log(String msg){
System.out.println("log:"+msg);
}
}
/*我们要做的事情:在转账方法(bankAction)执行之前进行一个日志输出*/
//前置通知
public class BeforeAdvice implements MethodBeforeAdvice {
//切面类
private MyLogger logger;
// 参数1 将来我们需要调用的目标对象中的方法镜像
// 参数2 将来调用方法的时候所传过来的参数
// 参数3 目标对象
//将来在调用目标对象方法之前,会先执行这个before方法
public void before(Method m, Object[] args, Object target) throws Throwable {
logger.log(m.getName() + " is invoked..");
/*
* 注意:这里一定不要自己手动的用反射去 调用这个目标对象中的方法,
* 因为spring 会帮我们去调用的,如果我们这个再去调用这个方法,
* 那么这这个方法会被调用俩次.
*
* m.invoke(target,args);
*
*/
}
get/set
}
配置xml文件: 注意ProxyFactoryBean的配置,htmlsingle中搜索即可
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao层对象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>beforeAdvice</value>
</list>
</property>
</bean>
2) Afteradvice 在目标方法执行完成以后被 织入
让一个类变成后置通知:实现AfterReturningAdvice接口
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable;
}
例如:
public class AfterAdvice implements AfterReturningAdvice {
private MyLogger logger;
//参数1 目标对象中的方法执行完返回值
//参数2 所执行方法的镜像对象
//参数3 执行方法时候所传的参数
//参数4 目标对象
//将来调用目标对象的方法之后会执行这个afterReturning方法
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
logger.log("after returning " + " target=" + target
+ " method Name=" + method.getName() + " args are:" + args
+ " returnValue=" + returnValue);
}
get/set
}
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="afterAdvice"
class="com.briup.aop.after.AfterAdvice">
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao层对象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl" />
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>afterAdvice</value>
</list>
</property>
</bean>
注意:另外一个返回后通知接口:AfterReturningAdvice的使用方式和这个是类似的,但是需要注意它们俩个之间的区别
3) 环绕Advice: 在目标方法执行之前和之后都 织入
实现MethodInterceptor接口
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
例如:
public class AroundAdvice implements org.aopalliance.intercept.MethodInterceptor{
private MyLogger logger;
public Object invoke(MethodInvocation mi) throws Throwable {
// mi.getMethod()获得将来要调用的方法的镜像
//在目标方法执行之前做日志
logger.log(mi.getMethod().getName() + " is start...");
// 这个方法就是用来调用目标对象中的方法的
Object returnValue = mi.proceed();
//在目标方法执行之后做日志
logger.log(mi.getMethod().getName() + " is end...");
return returnValue;
}
get/set
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="aroundAdvice"
class="com.briup.aop.around.AroundAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao层对象 -->
<bean name="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>aroundAdvice</value>
</list>
</property>
</bean>
4) Throws Advice 在目标方法执行抛异常的时候 织入
实现ThrowsAdvice接口
序列化
//ThrowsAdvice 是一个空接口,起标识作用
public interface ThrowsAdvice extends Advice {
}
例如:
public class ThrowingAdvice implements ThrowsAdvice {
private MyLogger logger;
public MyLogger getLogger() {
return logger;
}
public void setLogger(MyLogger logger) {
this.logger = logger;
}
//这里这个方法的名字一定要叫afterThrowing
//参数可以是1个也可以是四个
//1个参数的时候只能是一个异常类型的参数
//如果是4个参数的话,参数的顺序也一定要是下面的顺序
public void afterThrowing(Method method, Object[] args, Object target,Exception e) {
logger.log(e.getMessage());
}
//下面这样写也可以
/*
public void afterThrowing(Exception e) {
logger.log(e.getMessage());
}
*/
get/set
}
配置xml文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="throwAdvice" class="com.briup.aop.throwException.ThrowingAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao层对象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>throwAdvice</value>
</list>
</property>
</bean>
ioc : 控制反转
BeanFacotry
getBean("id/name")
getBean(Object.class)
ApplicationContext
ClassPathXmlApplicationContext
读取xml配置文件 创建ioc容器
AnnotationConfigApplicationContext
读取有注解的java类 创建ioc容器
xml :
对象放到容器中
<bean id="" class="全限定名">
给对象设置属性值:
setXxx:
<propety name="prop" value="">
<propety name="prop" ref="当前容器中的另一个bean对象">
构造器:
<constructor-arg index="0" type="int" value="10">
<constructor-arg index="1" type="int" value="10">
<constructor-arg index="2" type="int" value="10">
容器中对象默认是单例 :
每次从容器中获取 都是同一个对象
设置容器中的对象为非单例对象 :
<bean score="prototype">
每次从容器中获取 是不同对象
导入:<inport reource="路径/xxx.xml">
多xml创建ioc : ioc = new ClassPathXmlAC({"xx.xml","xxx.xml"})
生命周期 :
单例
创建 : 容器创建的时候会创建
初始化 : init-method="init"
使用 :
销毁 : 容器销毁的时候销毁,ioc.destory
非单例
创建 :获取的时候创建
初始化 : init-method="init"
容器不管理销毁工作
工厂 : 实现 FactoryBean 接口
ioc : 注解
使用注解配置
@Component : 组件
写在类名上,把类对象放到ioc容器中
等价于 : <bean>
@Autowired : 自动注入
写在属性上,构造器上,set方法上
从当前容器中获取对象注入进来
等价于 : <bean autowired="">
javaConfig配置ioc容器 :boot
@Configuration
类名上添加该注解,当前类是一个配置类(等价于xxx.xml)
@Bean
写在方法上,把返回值类型 的对象 放到容器中 id叫方法名
方法可以写参数,引用数据类型的参数会自动注入
@Import
@ComponentScan("组件注解的包")
A O P : 面向[切面]编程
切面 : (项目中很多有共同特点方法的集合)
简化代码:
【咱核心业务逻辑】
insert
delete
update
【spring做非核心业务逻辑】
转账 : service
//当前用户和 name 转 num多钱
zhuanZ(String name,Long num){
User user = session.getA("user");
User u = new User(name);
//user钱减少
// Dao
update user
set num = --
where name = 自己
//记录
//u钱++
// Dao
update user
set num = ++
where name = 小红
//记录
事务 手动提交
当整个业务逻辑全部完成 就要提交
}
zhuanz(String name,Long num){
自己--;
name++;
}
springLog(){
syso("打印日志")
}
springCommit(){
syso("提交事务")
}
代 理 模 式 :
静态 代理 :
目标类 和 代理类 都事先 编写好的
动态 代理 :
目标类 写好的 ,代理类 运行期间动态生成。
jdk动态代理 :
基于接口的动态代理
cglib 动态代理 :
代理类 继承 目标类
方法拦截器 :
6 Spring实现AOP(Aspect Oriented Programming)是依赖JDK动态代理和CGLIB代理(不同情况spring会自己选择一种方式)。
JDK动态代理和CGLIB代理的对比:
JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的。
所以spring会有以下俩种选择动态代理实现方式的情况:
*如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
*如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间自动选择;
7 认识AOP中的一些基本概念,然后在一个一个的例子中,不断的加强对这些概念的理解,同时要能自己表述出每个概念的含义
AOP 面向切面编程
目标方法 目标类中需要被执行的方法
aspect 切面/切面类
封装方法,需要通知带到连接点/切入点 前/后执行
joinPoint 连接点
在spring的aop中只有 类中的方法 可以做连接点,每一个方法都可以是一个连接点.
pointCut 切入点 增强器
一组连接点的集合
<aop:pointCut>
advice 通知/拦截器 : 携带切面 到目标方法执行前后
用来控制切面类将来到底是织入到切入点的前面、后面或者是抛异常的时候。
adivsor 增强器/选择器 : 把连接点 变成切入点
用来筛选类中的哪些方法是我们的连接点(哪些方法需要被拦截).
target 目标对象
proxy 代理对象
wave 织入 :
advice分五种:
把切面类织入到切入点(一堆连接点)
1,前置通知(Before advice):
实现接口MethodBeforeAdvice
在某连接点(join point)之前执行的通知
2,返回后通知(After returning advice):
实现接口AfterReturningAdvice
在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
3,抛出异常后通知(After throwing advice):
实现接口ThrowsAdvice
在方法抛出异常退出时执行的通知。
4,后置通知(After (finally) advice):
实现接口AfterAdvice
当某连接点退出的时候执行的通知
5,环绕通知(Around Advice):
实现接口MethodInterceptor
包围一个连接点(join point)的通知,例如事务的处理,就需要这样的通知,因为事务需要在方法前开启,在方法后提交
7 在Spring中,Advice是由spring中的几个接口来指定,主要有以下几种:
1) Before Advice 在目标方法执行前被 切面类 织入
普通的类变成前置通知:实现MethodBeforeAdvice接口
MethodBeforeAdvice接口:自定义类实现了这个接口 spring就会认为你是前置通知
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
2) Afteradvice 在目标方法执行完成以后被 织入
让一个类变成后置通知:实现AfterReturningAdvice接口
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable;
}
注意:另外一个返回后通知接口:AfterReturningAdvice的使用方式和这个是类似的,但是需要注意它们俩个之间的区别
3) 环绕Advice: 在目标方法执行之前和之后都 织入
实现MethodInterceptor接口
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
4) Throws Advice 在目标方法执行抛异常的时候 织入
实现ThrowsAdvice接口
序列化
//ThrowsAdvice 是一个空接口,起标识作用
public interface ThrowsAdvice extends Advice {
}
例如:
public class ThrowingAdvice implements ThrowsAdvice {
private MyLogger logger;
public MyLogger getLogger() {
return logger;
}
public void setLogger(MyLogger logger) {
this.logger = logger;
}
//这里这个方法的名字一定要叫afterThrowing
//参数可以是1个也可以是四个
//1个参数的时候只能是一个异常类型的参数
//如果是4个参数的话,参数的顺序也一定要是下面的顺序
public void afterThrowing(Method method, Object[] args, Object target,Exception e) {
logger.log(e.getMessage());
}
//下面这样写也可以
/*
public void afterThrowing(Exception e) {
logger.log(e.getMessage());
}
*/
get/set
}
配置xml文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="throwAdvice" class="com.briup.aop.throwException.ThrowingAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao层对象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>throwAdvice</value>
</list>
</property>
</bean>
8 advisor
作用:筛选要拦截目标对象的方法,之前的advice是把目标对象中的所有方法全部都进行代理
不要写java代码 ,只要配置
指定为advisor的接口为:
public interface PointcutAdvisor {
Pointcut getPointcut();
Advice getAdvice();
}
spring中已经给我们提供了实现类RegexpMethodPointcutAdvisor,在xml中直接配使用就可以了
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor 增强器-->
<!-- 作用:筛选要拦截(要代理)的方法 -->
<bean name="advisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice 表示增强器要在哪一个advice起作用-->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法(连接点) -->
<property name="patterns">
<list>
<value>.*bankAction</value> 这个地方 .*bank.* 就是以bank开头 或者这个结尾 但是这个bank在中间就不可以
</list>
</property>
</bean>
<bean id="dao"
class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice/advisor 可以有多个 -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
9 AutoProxy 自动代理:DefaultAdvisorAutoProxyCreator类的使用
使用原因:在配置文件中我们往往需要给很多个目标对象设置代理对象,那么上面例子的方式就需要每个目标对象的代理对象都需要配置一套类似的标签
自动代理:可以用很少的配置为xml文件中的目标对象自动的生成对应的代理对象
使用自动代理:
1,必须有增强器advisor,只有在增强器advisor中配置了哪些方法需要被拦截再然后织入
2,自动代理类(Spring)就会去IOC容器中找哪些bean中有增强器所拦截织入的方法。就代理这样的bean
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor -->
<!-- 作用:筛选要拦截的方法 -->
<bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice -->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法 -->
<property name="patterns">
<list>
<value>.*bankAction</value>
<value>.*deposit</value>
<value>.*withdraw</value>
</list>
</property>
</bean>
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target2"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target3"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用自动代理的方式 autoproxy -->
<!-- 注意:这不是一个工厂类,所以不能用过proxy来拿代理对象 -->
<bean name="proxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean>
<!--
使用自动代理的时候需要注意的方面:
1.当前的配置里面一定要有一个advisor的配置
2.不需要向自动代理类中注入任何信息
3.不管目标对象是否实现了一个或多接口,自动代理的方式
都能够为它产生代理对象(CGLib的方式).
4.从spring容器中拿代理对象的时候,需要通过目标对象的
名字来拿。
-->
10 AutoProxyByName 通过bean名字进行自动代理:BeanNameAutoProxyCreator类的使用
使用原因:虽然自动代理可以很方便的给xml文件中的目标对象设置对应的代理对象,但是并不是xml文件中的所有对象都是我们的目标对象,我们更想希望可以进一步筛选出某几个对象为我们的目标对象
通过bean名字进行自动代理:解决了上面的问题,给我们提供了筛选目标对象的配置方式
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor -->
<!-- 作用:筛选要拦截的方法 -->
<bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice -->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法 -->
<property name="patterns">
<list>
<value>.*bankAction</value>
<value>.*deposit</value>
<value>.*withdraw</value>
</list>
</property>
</bean>
<bean id="dao"
class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target2"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target3"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用自动代理的方式 autoproxybyname -->
<bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 注入需要被代理的对象名字 -->
<property name="beanNames">
<list>
<value>target</value>
<value>target2</value>
<value>dao</value>
</list>
</property>
<!-- 注入advice或者advisor -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
<!--
使用自动代理的时候需要注意的方面:
1.当前的配置里面有没有advisor的配置都没关系
2.需要向自动代理类中注入被代理目标对象的名字已经advice或者advisor
3.不管目标对象是否实现了一个或多接口,自动代理的方式
都能够为它产生代理对象.
4.从spring容器中拿代理对象的时候,需要通过目标对象的
名字来拿。
-->
第四天:
1 aop:config标签:主要是用来配置自动代理
1,加入schame文件
2,加入相应jar包
3,xml文件需要加入几句话...具体看文档
使用aop的专用标签来完成相关的配置.
需要引入一些相应的schame文件spring-aop-3.2.xsd
<aop:config> : 配置代理对象
筛选出切入点
把相应的切面带到切入点
1,<aop:pointcut>+<aop:advisor>
2,<aop:pointcut>+<aop:aspect>+<aop:after method=""/>
其中主要表现是使用Aspect的expression的操作:
execution(表达式):主要用来筛选连接点-->切入点
方法
权限修饰符 返回值 包 方法名 参数列表
public * com.briup.service.*.*(*)
表达式:
1,返回类型模式
* 任意的返回类型 返回类型模式决定了方法的返回类型必须依次匹配一个连接点。
2,名字模式
* 所有或者部分命名模式 名字模式匹配的是方法名,
3,参数模式
() 没有参数的方法
(..) 参数任意的方法
(*) 一个参数的方法
(*,String) 匹配两个参数,第一个参数任意,第二个必须为String
注意在使用之前需要在xml文件的beans标签中加入新的schame文件:并在Eclipse中进行关联配置
<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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
下面给出一些常见切入点表达式的例子。
1)任意包下的任意类中的公共方法的执行:
execution(public * *(..)) 公有的 返回值不限 方法名不限 参数任意
2)任何一个以“set”开始的方法的执行:
execution(* set*(..)) 所有的以set开头 参数任意的方法
3)AccountService 接口的任意方法的执行:
execution(* com.briup.service.AccountService.*(..)) 所有 这个类中方法参数不限
4)定义在service包里的任意方法的执行:
execution(* com.briup.service.*.find*(..)) 所有 这个包下所有类里面的所有方法
5)定义在service包或者子包里的任意方法的执行:
execution(* com.briup.service..*.*(..)) 所有 包下以及service的子包下所有类下所有方法 参数不限
eg:expression="execution(* com.briup.service.*.*(..)) or execution(* com.shiyue.service.*.*(..))"
筛选连接点-->切入点
注意: 1.从spring容器中拿代理对象的时候也是要用目标对象的名字来拿。
2.没有实现任何接口的目标对象也能产生代理对象。
<!-- 配置aop的代理 -->
<aop:config>
<!-- 定义一个切入点 并给切入点起名为myPointCut -->
<!-- 切入点是一组连接点的集合 -->
<aop:pointcut expression="execution(public * com.briup.aop.service.*.*(..))" id="myPointCut"/>
<!-- 定义哪一个advice在哪一个切入点上面起作用 -->
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="myPointCut" />
</aop:config>
织入: 切面 --> 切入点
<!--
expression="execution(public * com.briup.aop.service.*.*(..))"
这个引号""里面就是用表达式的方式来定义切入点,只要是符合我们这个表达式要求的
方法就是我们的连接点,连接点的集合就是我们要定义的切入点。
表达式中从左到右的*号:
第一个* 表示方法的返回类型不限。
第二个* 表示包中的任意一个类
第三个* 表示类中的任意一个方法
同时方法的参数也没有限制.
-->
2 在一个切面类中定个多个方法,根据xml文件的配置每个方法都可以织入到切入点的不同位置,并且advice是在aop的标签中进行配置,不需要再写对应的advice类了
例如:
//这个类相当于我们之前的切面类
//只不过这个切面类中有很多方法都可以织入到切入点上面
//我们可以控制把这里的任何一个方法织入到任何一个切入点上面
public class XmlHandler {
public void beforeTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" before...");
}
public void afterTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" after...");
}
public void afterReturningTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" afterReturning");
}
//在和aroundAdvice结合的时候,这个方法一定要加上这个ProceedingJoinPoint类型的参数
public Object aroundTest(ProceedingJoinPoint pjp)throws Throwable{
//JoinPoint对象不能调用连接点所表示的方法
//ProceedingJoinPoint能调用连接点所表示的方法 pjp.proceed()
System.out.println(pjp.getSignature().getName()+" is start..");
//调用到连接点方法
Object obj = pjp.proceed();
System.out.println(pjp.getSignature().getName()+" is end..");
return obj;
}
public void throwingTest(JoinPoint p,Exception ex){
System.out.println(p.getSignature().getName()+" is throwing..."+ex.getMessage());
}
}
xml文件配置:
<!-- 配置dao层对象 -->
<bean id="dao"
class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置切面类 -->
<bean name="handler" class="com.briup.aop.xml.XmlHandler"></bean>
<!-- 配置aop的代理 -->
<aop:config>
<!-- 定义切入点名为myPointCut -->
<aop:pointcut expression="execution(public * com.briup.aop.service.*.*(..))" id="myPointCut"/>
<!-- 定义切面类 以及需要使用的advice -->
<aop:aspect id="aspect" ref="handler">
<!-- 表示beforeAdvice会把切面类handler中的beforeTest方法织入到名字叫myPointCut的切入点上面 -->
<aop:before method="beforeTest" pointcut-ref="myPointCut"/>
<!-- after表示不管方法是否正常结束都会起作用 -->
<aop:after method="afterTest" pointcut-ref="myPointCut"/>
<!-- after-returning表示方法正常结束才会起作用(抛异常时候不起作用) -->
<aop:after-returning method="afterReturningTest" pointcut-ref="myPointCut"/>
<aop:around method="aroundTest" pointcut-ref="myPointCut"/>
<!-- throwing="ex"表示throwingTest方法中接收异常对象的名字一定要是ex -->
<aop:after-throwing method="throwingTest" pointcut-ref="myPointCut" throwing="ex"/>
</aop:aspect>
</aop:config>
3 使用注解配置AOP:其实就是在上面的类XmlHandler中加入上注解然后去掉xml中的aop标签配置,这里把类改名为AnnotationHandler,
切面类:@Aspect
写在类上
切入点: @Pointcut 空方法是@载体
写一个空方法:因为@Pointcut-->id-->空方法名()
通知: @Before,@After,@AfterReturning,@Around
切面的方法
<aop:aspectj-autoproxy/>:如果使用了AOP有关注解 需要这个标签标示
例子:
@Component
@Aspect//标示这个类是切面类 写在类上
public class AnnotationHandler {
/*
* 在一个方法上面加上注解来定义切入点
* 这个切入点的名字就是这个方法的名字
* 这个方法本身不需要有什么作用
* 这个方法的意义就是:给这个 @Pointcut注解一个可以书写的地方
* 因为注解只能写在方法、属性、类的上面,并且方法名作为切入点的名字
* @Pointcut 注解 写在方法上,""中写切入点表达式
* */
@Pointcut("execution(public * com.briup.aop.service..*.*(..))")
public void myPointCut(){}
//注:这里面的所有方法的JoinPoint类型对象都可以去掉不写
@Before("myPointCut()")// 前置通知 携带下面方法 织入到切入点
public void beforeTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" before...");
}
/*
* @After和@AfterReturning
*
* @After标注的方法会在切入点上的方法结束后被调用(不管是不是正常的结束).
* @AfterReturning标注的方法只会在切入点上的方法正常结束后才被调用.
* */
@After("myPointCut()")
public void afterTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" after...");
}
@AfterReturning("myPointCut()")
public void afterReturningTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" afterReturning");
}
@Around("myPointCut()")
public Object aroundTest(ProceedingJoinPoint pjp)throws Throwable{
System.out.println(pjp.getSignature().getName()+" is start..");
//调用连接点的方法去执行
Object obj = pjp.proceed();
System.out.println(pjp.getSignature().getName()+" is end..");
return obj;
}
//在切入点中的方法执行期间抛出异常的时候,会调用这个 @AfterThrowing注解所标注的方法
@AfterThrowing(value="myPointCut()",throwing="ex")
public void throwingTest(JoinPoint p,Exception ex){
System.out.println(p.getSignature().getName()+" is throwing..."+ex.getMessage());
}
}
xml配置:注意给例子中使用的其他的类上面也使用注解
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.briup.aop"/>
aop :
面向切面编程
切面 :项目中很多具有共同特点方法的集合
核心 : 代理
静态代理
目标类 和 代理类 都事先准备好
动态代理
目标类 有
代理类 代码运行期间 动态生成
jdk : 基于接口,
代理类和目标类 实现相同的接口
看起来方法一样
cglib : 基于继承
代理类 继承 目标类
spring aop 编程 集成了 jdk 和 cglib 两种代理
spring会自动选择
目标类 : 实现了接口 spring选jdk
目标类 : 没有实现了接口 spring选cglib
aop 基础名词 :
目标类(委托类)
目标对象
目标方法
代理类
代理对象
joinPoint : 连接点
目标类中所有方法都是连接点
pointcut : 切入点
目标类中部分方法集合是切入点
ascept : 切面类
代码封装在这个类中
advice :通知/拦截器
把代码 携带 到目标方法前后 执行
advisor : 增强器
把通知加强
把代码 携带 到 【部分】 目标方法前后 执行
weaving: 织入
通知 携带 代码 去 目标方法 前后执行的过程
目标类 :
1,核心业务逻辑 + 非核心业务逻辑
2,目标类中的方法 不想完成 非核心业务逻辑
3,非核心业务交给spring处理
service 业务逻辑
日志记录
提交事务 回滚事务
advice :通知
1,前置通知(Before advice):
实现接口MethodBeforeAdvice
在某连接点(join point)之前执行的通知
2,返回后通知(After returning advice):
实现接口AfterReturningAdvice
在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
3,抛出异常后通知(After throwing advice):
实现接口ThrowsAdvice
在方法抛出异常退出时执行的通知。
4,后置通知(After (finally) advice):
实现接口AfterAdvice
当某连接点退出的时候执行的通知
5,环绕通知(Around Advice):
实现接口MethodInterceptor
包围一个连接点(join point)的通知,例如事务的处理,就需要这样的通知,因为事务需要在方法前开启,在方法后提交
AOP 编程 需要 配置得到代理对象
使用代理对象 调用 目标方法
增强器 :对代理的目标方法进行控制
把通知加强
最后
以上就是眯眯眼镜子为你收集整理的Spring课程学习笔记-Spring介绍的全部内容,希望文章能够帮你解决Spring课程学习笔记-Spring介绍所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复