Spring
什么是Spring?
1.Spring 是一个开源框架。
2.Spring是一个业务逻辑层框架。
2.Spring 是一个 IOC(DI) 和 AOP 容器框架。
3.Spring的优良特性:
1.非侵入性(轻量级):基于Spring开发的应用中的对象可以不依赖于Spring的API。
2.依赖注入:**DI——反转控制(IOC)最经典的实现**。 IOC 是思想,DI是实现。
3.面向切面编程:**AOP**。
4.容器:Spring是一个容器,因为它**包含并且管理应用对象的生命周期**。
5.组件化:Spring实现了使用简单的组件配置合成一个复杂的应用。可以使用XML和Java注解组合这些对象。
6.一站式:在IOC和AOP的基础上可以整合各种第三方的类库。(实际上Spring自身也提供了 表述层SpringMVC 和 持久层SpringJDBC)
Spring底层使用的反射获取对象。
搭建Spring
1.加入jar包:
日志jar包:
commons-logging-1.1.1.jar
Spring自身jar包:
spring-beans-4.0.0.RELEASE.jar<br />
spring-context-4.0.0.RELEASE.jar<br />
spring-core-4.0.0.RELEASE.jar<br />
spring-expression-4.0.0.RELEASE.jar
2.如果开发工具自身创建了 Spring配置文件就无需创建,如果没有则自己创建(Spring Config XML)
Spring简单案例
1..创建 Spring Config XML 文件,文件名:applicationContext.xml (默认文件名)
applicationContext.xml:
<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里的命名空间:规定当前XML 里能写什么,不能写什么-->
<!--bean:定义Spring所管理的一个对象
class :指明对象的所属类(全限定名,全类名)
id:唯一标识Bean(对象),不能重复。在通过类型获取bean的过程中可以不设置
根据class属性可知,Spring底层就是使用反射获取的对象-->
<bean class="com.guolian.Spring.mod.Person" id="person">
<!--property:为对象的某个属性赋值
name:属性名
value:属性值-->
<property name="id" value="111"></property>
<property name="name" value="guolian"></property>
</bean>
<!--如果有多个Person对象,则必须给id赋值,不然会报错-->
<bean class="com.guolian.Spring.mod.Person" id="person2">
<!--property:设置属性 name:属性名 value:属性值-->
<property name="id" value="222"></property>
<property name="name" value="郭联"></property>
</bean>
</beans>
2.在 java里使用:
public class TestBySpring {
public static void main(String[] args){
//1.初始化容器
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过getBean()获取对象
//通过id获取bean
// Person person = (Person) ac.getBean("person");
//通过类型获取bean
//使用此方法获取对象时,要求Spring所管理的此类型的对象只能有一个
// Person person = ac.getBean(Person.class);
//通过id和类型获取bean
//尽可能的使用这个重载方法。可以更准确的获取到对象。
Person person = ac.getBean("person", Person.class);
System.out.println(person);
}
}
IOC容器和Bean的配置
1.IOC和DI:
IOC:反转控制(控制反转)
反转控制:把程序员对 对象的控制权,反转给程序本身,让程序自己管理对象。
反转了资源的获取方向:该由容器主动将资源推送给需要的组件。
开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可。
DI:依赖注入
把具有依赖关系的属性进行赋值。
IOC的另外一种表述方式:即组件以一些预先定义好的方式,接受来自于容器的资源注入。
**IOC就是一种反转控制的思想,而DI是对IOC的一种具体实现**。
IOC容器在Spring中的实现
Spring中有IOC思想,而IOC思想必须基于IOC容器来完成。而IOC容器在最底层实质上就是一个对象工厂。
Spring提供了IOC容器的两种实现方式:
1.BeanFactory:IOC容器的基本实现,面向Spring本身,不提供开发人员使用。
2.ApplicationContext:BeanFactory的子接口,提供了更多高级特性,面向Spring的使用者。
ApplicationContext的主要实现类:
ClassPathXMLApplicationContext:对应类路径下的XML格式的配置文件(写相对路径)
FileSystemXMLApplicationContext:对应文件系统中XML格式的配置文件(写绝对路径)
在初始化时就创建单例的bean,可以通过配置的方式指定创建多实例的bean。
2.给bean的属性赋值:
Spring默认bean 是单例的。
2.1依赖注入的方式
<!--
scope:设置设计模式(单例、多例、properties)
index:注入值在形参里的索引
ref:引用当前Spring里一个对象的id
type:注入值的类型
name:参数名
value:注入值-->
2.1.1通过bean的setXXX()方法赋值
<bean id="s1" class="com.guolian.di.Student">
<!--它们底层就是使用的bean类里的setXXX()方法赋值-->
<property name="id" value="10000"></property>
<property name="sex" value="男"></property>
<property name="name" value="郭联"></property>
<property name="age" value="19"></property>
<!--ref:引用,引用当前Spring所管理的一个bean对象的id-->
<property name="teacher" ref="t1"></property>
</bean>
2.1.2通过bean的构造器赋值
<bean id="s2" class="com.guolian.di.Student">
<!-- 自动匹配到类中相对应的构造方法 -->
<constructor-arg value="10086"></constructor-arg>
<constructor-arg value="泽野弘之"></constructor-arg>
<!--指定这个参数,是第3个形参的值,类型是Integer,可以用于指定相应的构造器-->
<constructor-arg value="30" index="2" type="java.lang.Integer">
</constructor-arg>
<constructor-arg value="男"></constructor-arg>
</bean>
2.2P命名空间
需要在 beans里额外加入这两行代码引入p命名空间,context必须在p前面
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
<?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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="s3" class="com.guolian.di.Student" p:id="10033" p:name="张三" p:age="35" p:sex="男" p:teacher-ref="t1">
</bean>
</beans>
2.3可以注入的值
2.3.1字面量
1.可以使用字符串表示的值,可以通过value属性或value子节点的方式指定
value子节点: <property name="id">
<value>10010</value>
</property>
value属性:<property name="sex" value="男"></property>
2.基本数据类型及其封装类、String等类型,都可以使用字面量注入的方式
3.若字面值中包含特殊字符,可以使用<![CDATA[]]> 把字面值包裹起来
2.2.2 null
2.2.3给bean的级联属性赋值
<bean id="s4" class="com.guolian.di.Student">
<property name="name" value="法外狂徒"></property>
<property name="age" value="30"></property>
<!--获得 teacher类的对象-->
<property name="teacher" ref="t1"></property>
<!--给teacher 里的 name 属性赋值-->
<property name="teacher.name" value="小红"></property>
</bean>
2.2.4 引用其他bean
<!--通过 ref 属性 获取其他的bean。 里面写其他bean的id-->
<property name="teacher" ref="t1"></property>
2.2.5内部bean
在谁的内部定义的bean,它就只属于谁,在其他的bean里引用不到
<bean id="s5" class="com.guolian.di.Student">
<property name="id" value="16666"></property>
<property name="sex" value="男"></property>
<property name="name" value="s5永远的18岁"></property>
<property name="age" value="18"></property>
<property name="teacher">
<!--在谁的内部定义的bean,它就只属于谁,在其他的bean里引用不到-->
<bean id="st1" class="com.guolian.di.Teacher">
<property name="tid" value="22222"></property>
<property name="name" value="admin"></property>
</bean>
</property>
</bean>
2.4集合属性
在Spring中可以通过一组内置的XML标签来配置集合属性。例如:、或
数组的定义和list一样,都使用 标签,也可以使用 标签
2.4.1 list
<!--List 里写入字面量-->
<bean id="t2" class="com.guolian.di.Teacher">
<property name="tid" value="100006"></property>
<property name="name" value="老师二号"></property>
<property name="cls">
<list>
<value>a</value>
<value>b</value>
<value>c</value>
</list>
</property>
</bean>
<!--List 里 写入 bean-->
<bean id="t3" class="com.guolian.di.Teacher">
<property name="tid" value="100006"></property>
<property name="name" value="老师二号"></property>
<property name="students">
<list>
<ref bean="s1"></ref>
<ref bean="s2"></ref>
<ref bean="s3"></ref>
</list>
</property>
</bean>
2.4.2 map
<bean id="t4" class="com.guolian.di.Teacher">
<property name="tid" value="100124"></property>
<property name="name" value="老师四号"></property>
<property name="bossMap">
<map>
<entry key="10001" value="领导一号"></entry>
<entry key="10002" value="领导二号"></entry>
</map>
</property>
</bean>
2.4.3 集合类型的bean
<bean id="t5" class="com.guolian.di.Teacher">
<property name="tid" value="100125"></property>
<property name="name" value="老师五号"></property>
<property name="students" ref="students"></property>
</bean>
<util:list id="students" >
<ref bean="s1"></ref>
<ref bean="s2"></ref>
<ref bean="s3"></ref>
<ref bean="s4"></ref>
</util:list>
<util:map id="map">
<entry key="1" value="张三"></entry>
</util:map>
2.5 FactoryBean
Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean。
工厂Bean:
帮忙创建对象的对象。
创建一个类实现FactoryBean,当我们获取它的对象时,获取的是它getObject()里返回的对象,而不是它本身的对象。
工厂Bean:
public class MyFactory implements FactoryBean<Car>{
@Override
public Car getObject() throws Exception {
Car car = new Car();
car.setBrand("奥迪");
car.setPrice(20000.00);
return car;
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
配置工厂Bean
<bean id="factory" class="com.guolian.Spring.factorybean.MyFactory"></bean>
测试:
public class Test {
public static void main(String[] args){
ClassPathXmlApplicationContext classPathXmlApplicationContext =
new ClassPathXmlApplicationContext("factorybean.xml");
Object factory = classPathXmlApplicationContext.getBean("factory");
System.out.println(factory);
//输出的 Car的对象,而不是MyFactoy的对象
}
}
2.6 Bean的作用域
Spring 所创建的bean 默认是单例。可以通过 scope 属性更改。prototype:多例。
若Spring中有单例模式的Bean,则当Spring容器初始化时,就会创建单例bean的对象。
而多例(原型)bean,会在使用时创建其对象。
<bean id="s1" class="com.guolian.Spring.scope.Student" scope="prototype">
<property name="sid" value="1001"></property>
<property name="sname" value="张三"></property>
</bean>
2.7 Bean的生命周期
Spring IOC容器对bean 的生命周期进行管理的过程:
1.通过构造器或工厂方法创建bean 实例。
2.为bean的属性设置值和对其他bean的引用(依赖注入)
3.调用bean的初始化方法
4.bean可以使用了
5.当容器关闭时,调用bean的销毁方法
<!--init-method 和 destroy-method 定义了当前对象初始化和销毁时被调用的方法。
需要在类里创建两个方法,名字随意。-->
<bean id="person" class="com.guolian.Spring.IOC.life.Person" init-method="init" destroy-method="destory">
<property name="id" value="10001"></property>
<property name="sex" value="男"></property>
<property name="name" value="张三"></property>
</bean>
public class Test {
public static void main(String[] args){
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("spring-beanlife.xml");
Person person = ac.getBean("person", Person.class);
System.out.println(person);
//关闭容器,调用销毁方法。
ac.close();
}
}
2.7.2bean的后置处理器
1.bean的后置处理器允许在调用初始化方法前后对bean进行额外的处理。
2.bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例
3.bean的后置处理器需要实现接口:BeanPostProcessor,在初始化方法调用前后,Spring将把每个bean实例分别传递给其中实现的两个方法。
只要在XML配置文件里加入了后置处理器,它就会自动的对当前页面内的每一个bean进行处理。
<bean class="com.guolian.Spring.IOC.life.AfterHandler"></bean>
public class AfterHandler implements BeanPostProcessor {
@Override
//在初始化之前对bean进行操作
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//因为Spring里就一个 person类的bean,所以强转一下。
Person person = (Person) bean;
if (person.getSex().equals("男")){
person.setName("周杰伦");
}else {
person.setName("宋祖英");
}
//返回处理之后的新的bean
return person;
}
@Override
//初始化之后对bean进行操作。
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//没有进行任何操作,返回传入的bean。
return bean;
}
}
2.8引用外部属性文件
当bean的配置文件过多时,可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用它。
引用外部文件数据固定格式:${}
两种方式选一种即可:
<!--引用外部配置文件-->
<!--方式1:使用bean标签引入外部配置文件-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="com/guolian/Spring/datasource/db.properties"></property>
</bean>
<!--方式2:使用context标签引入外部配置文件-->
<context:property-placeholder location="com/guolian/Spring/datasource/db.properties"></context:property-placeholder>
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<!--引用db.properties 文件里的数据-->
<!--引用外部文件数据固定格式:${}-->
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
2.9自动装配和兼容性
自动装配:自动注入
手动装配:以value或ref的方式明确指定属性值
自动装配:根据指定的装配规则,不需要明确指定。Spring自动将匹配的属性值注入bean中。
2.9.1autowire:
设置自动装配<br />
autowire="byName" 根据**_属性名称和bean的id_**自动装配 (bean的id跟属性名一致,就会自动赋值)
autowire="byType" 根据**_属性类型和bean的类型_**自动装配 (bean的类型跟属性的类型**兼容**,就会自动赋值) byType要求:**Spring容器中只能有一个能为属性赋值的bean**。
选用建议: 只要设置了autowire属性,会作用于该bean 中所有的非字面量值。
因此,谁都不用。
2.9.2兼容性
如果使用 byType 自动装配,那么它们**不仅仅只能给相同的类型赋值**。**还能给它们的父类或实现的接口类赋值**。这就是兼容性。
<!-- autowire:设置自动装配
autowire="byName" 根据名称(name)自动装配(bean的id跟属性名一致,就会自动赋值) -->
<bean id="emp" class="com.guolian.Spring.IOC.auto.Emp" autowire="byName">
<property name="eid" value="1001"></property>
<property name="ename" value="张三"></property>
</bean>
<bean id="car" class="com.guolian.Spring.IOC.auto.Car">
<property name="cid" value="666"></property>
<property name="cname" value="霸道"></property>
</bean>
<bean id="dept" class="com.guolian.Spring.IOC.auto.Dept">
<property name="did" value="111111"></property>
<property name="dname" value="开发部"></property>
</bean>
<!-- autowire="byType" 根据类型自动装配(bean的类型跟属性的类型一致,就会自动赋值)
限制条件:当前配置页面内,不能有相同的类型
兼容性:这些类还可以为它们的父类或实现的接口类赋值-->
<bean id="emp2" class="com.guolian.Spring.IOC.auto.Emp" autowire="byType">
<property name="eid" value="1001"></property>
<property name="ename" value="张三"></property>
</bean>
<bean id="car2" class="com.guolian.Spring.IOC.auto.Car">
<property name="cid" value="666"></property>
<property name="cname" value="霸道"></property>
</bean>
<bean id="dept2" class="com.guolian.Spring.IOC.auto.Dept">
<property name="did" value="111111"></property>
<property name="dname" value="开发部"></property>
</bean>
2.10 通过注解配置bean
注解的配置相对于XML 更加的简洁和优雅。但是后期修改更新不容易。
2.10.1使用注解标识组件
1.普通组件:@Component
标识一个受Spring IOC 容器管理的组件
2.持久化层组件:@Repository
标识一个受Spring IOC 容器管理的持久化层组件
3.业务逻辑层组件:@Service
标识一个受Spring IOC 容器管理的业务逻辑层组件
4.表述层控制器(servlet)组件:@Controller
标识一个受Spring IOC 容器管理的表述层控制器组件
5.context:component-scan:扫描组件,加载注解,在Spring生成对应的Bean,
这些bean 的id 会以类的首字母小写为值
6.以上注解都可以加上:@Component(value = “userController”),自定义加载到Spring里的id。
@Component(value = “userController”) = @Component( “userController”)
两步走:1.在Java类中加上对应注解 2.在Spring配置文件中扫描组件
@Service
public class UserServiceImpl implements UserService{}
@Repository
public class UserDAOImpl implements UserDAO {}
@Controller
public class UserController {}
<!--扫描指定的 包package ,加载组件-->
<!--context:component-scan:扫描组件,加载注解,在Spring生成对应的Bean,
这些bean 的id 会以类的首字母小写为值-->
<!--组件:指Spring 中管理的Bean-->
<context:component-scan base-package="com.guolian.Spring2.annotate"></context:component-scan>
2.10.2扫描组件时的包含和排除
默认的过滤器:扫描所有类。
包含要将默认的过滤器关闭:关闭过滤器,然后再放进来,增加效率。
排除要将默认的过滤器打开:只有先扫描了所有的类,才能进行排除。
包含:
<!--use-default-filters="false":将默认的过滤器关闭,不然将使用默认的过滤-->
<context:component-scan base-package="com.guolian.Spring2.annotate" use-default-filters="false">
<!--context:include-filter:除了指定的类,其他的都过滤掉-->
<!-- annotation :根据注解包含, 后面写注解所在类的全类名-->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"> </context:include-filter>
<!-- assignable:根据类型包含 ,后面写要包含类的全类名-->
<context:include-filter type="assignable"
expression="com.guolian.Spring2.annotate.service.UserServiceImpl"> </context:include-filter>
</context:component-scan>
排除:
<!--use-default-filters="true":将默认的过滤器开启,扫描所有的类-->
<context:component-scan base-package="com.guolian.Spring2.annotate" use-default-filters="true">
<!--context:exclude-filter:过滤掉指定的类-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"></context:exclude-filter>
<context:exclude-filter expression="com.guolian.Spring2.annotate.service.UserServiceImpl" type="assignable"></context:exclude-filter>
</context:component-scan>
2.10.3基于注解的自动装配
1.@Autowired 会默认使用:byType 如果byType装配不了,就会使用 byName
在需要装配的变量上,加上注解 @Autowired,然后在 Spring扫描组件,装配到该变量。
2.@Qualifier(value = “要装配的bean的id”):限定符,如果有多个bean可以装配到该变量,则使用限定符限定,只让某一个bean装配该属性。
需要先装配,再限定。
Spring会自动的扫描组件,用相应的类型去装配属性。
public class UserController {
//在Spring IOC 容器中扫描于 UserServiceImpl相匹配的bean,然后装配该变量
@Autowired
@Qualifier(value = "userServiceImpl")
private UserServiceImpl userService;
@Autowired
private UserServiceImpl userService1;
}
public class UserServiceImpl implements UserService{
//在Spring IOC 容器中扫描于 UserDAOImpl相匹配的bean,然后装配该变量
@Autowired
private UserDAOImpl userDAO;
}
AOP前奏
日志:在程序执行期间追踪正在发生的功能。(向控制台输出一条相应的信息)
3.1动态代理
实现的 jdk动态代理,依赖于接口。
还有一种CGLIB动态代理,依赖于继承。
具体实现请回顾反射。
public class ProxyUtil {
public Object getProxy(Object obj){
ClassLoader classLoader = this.getClass().getClassLoader();
Class<?>[] interfaces = obj.getClass().getInterfaces();
//第一个参数:获取类的加载器(什么类的加载器都行,不用一定要被代理类的)
//第二个参数:获取被代理类实现的一套或一个接口,为了了解被代理类所实现的功能。
//第三个参数:设置代理对象如何实现目标对象的功能。实现接口InvocationHandler。
return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
@Override
//代理对象实现功能的方式。
//第一个参数:代理类(不使用)
//第二个参数:被调用的方法
//第三个参数:被调用方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前");
//method.invoke :实现了方法的调用(非常重要)
//那么我们就可以在调用所有的被代理方法前后加入一些功能
Object invoke = method.invoke(obj, args);
System.out.println("后");
return invoke;
}
});
}
public static void main(String[] args){
ProxyUtil proxyUtil = new ProxyUtil();
MathImpl math = new MathImpl();
//不能直接转换为被代理类:会出现类型转换异常(因为传到代理类里的是接口)
//需要转换为被代理类实现的接口
Math proxy = (Math) proxyUtil.getProxy(math);
proxy.add(1,2);
}
}
3.2AOP概述
1.AOP:面向切面编程,是一种新的方法论,是对传统OOP(面向对象编程)的补充。
OOP:纵向继承机制 (找到对象,调用功能方法)
AOP:横向抽取机制(把一些公共的功能,非业务的代码抽取出来)
2.AOP编程操作的主要对象是切面,而切面用于模块化横切关注点(公共功能)。
3.切面:存储非业务代码和公共功能的类。 横切关注点:非业务代码、公共功能。
4.AOP图解:AOP的实现依赖于动态代理
3.3AOP术语
1.横切关注点:
每个方法里的 非业务代码、公共功能(日志功能、验证功能.......)。
2.切面:
封装横切关注点的类,**每个关注点体现为一个通知方法**。
3.通知:
切面必须完成的各个工作。(**把横切关注点放到切面,它就叫通知**)
前置通知、后置通知、返回通知、异常通知、环绕通知(包含前四种通知)
4.目标:
被通知的对象。(抽取出来的代码**要作用到的对象**)
5.代理:
向目标对象应用通知之后,创建的代理对象。
6.连接点:
横切关注点在程序代码中的具体体现,对应目标程序执行的某个特定位置。(如:方法调用前后、异常捕获之后、finally)
7.切入点:
定位连接点的方式。AOP 可以通过切入点定位到特定的连接点。
只有写了切入点,切面才能找到它要作用的位置。
切点通过 **org.springframework.aop.Pointcut; 接口进行描述**,它使用**类和方法作为连接点的查询条件**。
**切入点表达式**:execution(public int 切入类的全类名.方法(int,int))
8.图解:
3.4AspectJ
AspectJ:Java社区里最完整最流行的**AOP框架**。它不属于Spring
Spring2.0以上:可以使用基于 AspectJ 注解或XML配置的AOP。
3.4.1在Spring启用AspetJ注解
1.导入Jar包
com.springsource.net.sf.cglib-2.2.0.jar(动态代理的模式,如果jdk不行可以使用CGLIB)
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
3.4.2简单使用AspectJ
AspectJ的自动代理底层使用的是:动态代理。
AOP 依赖于 IOC 。所以使用AOP的注解时,要在上面加上IOC的组件注解,告诉IOC容器加载它们。
@Component
@Aspect
//标注当前类为切面
public class MyloggerAspect {}
1.需要在配置文件中配置 组件扫描。和开启 AspectJ的自动代理。
2.在切面上定义注解(组件注解和切面注解)
3.在切面中定义通知。并在通知上加上注解,并定义其切入点,告诉IOC容器这是一个通知。
4.切入点:@Befor(value=”public int 切入类的全类名.切入方法(形参列表)”)
5.AOP底层使用的动态代理,所以在获取bean时,类型需要用其实现的接口类型。
XML 配置:
<!--开启AspectJ的自动代理功能 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--扫描组件,加载注解-->
<context:component-scan base-package="com.guolian.Spring2.AOP.AspectJ"></context:component-scan>
切面类:
/**
* 切面:存储横切关注点的类
*/
@Component
@Aspect
//标注当前类为切面
public class MyloggerAspect {
//通知
/**
* @Before:将方法指定为前置通知
* 必须设置 value 其值为切入点表达式
*/
@Before(value = "execution(public int com.guolian.Spring2.AOP.AspectJ.MathImpl.add(int,int))")
private void beforeMethod(){
System.out.println("这是一个前置通知");
}
}
目标类:
@Component
public class MathImpl implements Math {......}
测试类:
public class Test {
public static void main(String[] args){
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("com/guolian/Spring2/AOP/AspectJ/aop-aspectj.xml");
//因为AOP底层是使用的动态代理,所以需要用接口类,而不是实现类。
Math mathImpl = ac.getBean("mathImpl", Math.class);
int add = mathImpl.add(1, 2);
System.out.println(add);
}
}
3.4.3前置通知
作用于:方法执行之前
JoinPonint :连接点。
@Before(value = "execution(public int com.guolian.Spring2.AOP.AspectJ.MathImpl.add(int,int))")
private void beforeMethod(JoinPoint joinPoint){
//JoinPoint 连接点
//获取方法的参数
Object[] args = joinPoint.getArgs();
//getSingnature :签名,对一些名字的封装
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println("method:"+name+",arguments:"+ Arrays.toString(args));
}
3.4.4切入点表达式
@Before(value = "execution(public int com.guolian.Spring2.AOP.AspectJ.MathImpl.add(int,int))")
// 标准的切入点表达式: 访问修饰符 返回值 全类名.方法名(形参列表)
@Before(value = "execution(* com.guolian.Spring2.AOP.AspectJ.*.*(..))")
//第一个* :代表任意的 访问修饰符和返回值
//第二个* :代表任意的 AspectJ下的类
//第三个* :代表任意的 AspectJ下的方法
//(..) :代表任意的 形参列表
3.4.5后置通知
作用于:方法的 finally 语句块,即不管方法有没有异常,都会执行。
@After(value = "execution(* com.guolian.Spring2.AOP.AspectJ.*.*(..))")
private void after(JoinPoint joinPoint){
System.out.println("后置通知");
}
}
3.4.6返回通知
@AfterReturning 将方法标注为返回通知。
作用于:方法执行之后。相当于:写在了try 代码块的最后一行。如果出现异常,则不执行
returning = “” :设置接收方法返回值的变量名。类型为Object
要想在方法中使用返回值,必须在方法的形参中设置和变量名相同的参数名。
//returning = "result" :定义一个Object类型的参数名,接收方法的返回值
@AfterReturning(value = "execution(* com.guolian.Spring2.AOP.AspectJ.*.*(..))",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
//将注解里,接收的返回值写入形参列表,供方法使用
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println("method:"+name+",result:"+result);
}
3.4.7异常通知
/**
*
* @AfterThrowing: 将方法标注为 异常通知(例外通知)
* 作用于:方法抛出异常时 相当于:写在了catch 代码块里。
* 通过 throwing 接收方法返回的异常信息
* 在形参列表中,可通过 具体的异常类型 对指定的异常信息进行操作
*/
@AfterThrowing(value = "execution(* com.guolian.Spring2.AOP.AspectJ.*.*(..))",throwing = "ex")
public void afterthrowingMethod(Exception ex){
//可以根据形参列表,处理指定的异常
System.out.println("有异常了,messages:"+ex);
}
3.4.8环绕通知
环绕通知 与 动态代理 类似。
@Around(value = "execution(* com.guolian.Spring2.AOP.AspectJ.*.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
Object result = null;
try {
//前置通知
System.out.println("前置通知");
//执行方法:相当于代理里的 method.invoke()
result = joinPoint.proceed();
//返回通知
System.out.println("返回通知");
//返回方法执行结果。
return result;
} catch (Throwable throwable) {
throwable.printStackTrace();
//异常通知
System.out.println("异常通知");
}finally {
//后置通知
System.out.println("后置通知");
}
return -1;
}
3.4.9公共切入点
重用切入点
@Pointcut(value = "execution(* com.guolian.Spring2.AOP.AspectJ.*.*(..))")
public void test(){ }
//引用了 test() 类的 切入点
@Around(value = "test()")
public Object aroundMethod(ProceedingJoinPoint joinPoint){}
@After(value = "test()")
private void after(JoinPoint joinPoint){}
@AfterThrowing(value = "test()",throwing = "ex")
public void afterthrowingMethod(Exception ex){}
3.4.10切面优先级
切面的优先级:确定哪个切面先执行
value 里放一个整型,默认值为 int 的最大值。不设置时,先定义的切面先执行。
值越小,优先级越高,越先执行。
//定义切面的优先级
@Order(value = 100)
@Order(value = 1)
//1 比 100 先执行
3.4.10 使用XML配置AOP
使用Spring 原生的 XML 配置AOP
<!--扫描i组件-->
<context:component-scan base-package="com.guolian.Spring2.AOP.xml"></context:component-scan>
<!--配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect ref="myLogger">
<!--配置通知的位置和切入点表达式-->
<!--方式一:先创建一个切入点表达式,然后引用-->
<aop:pointcut id="cut" expression="execution(* com.guolian.Spring2.AOP.xml.*.*(..))"></aop:pointcut>
<aop:before method="before" pointcut-ref="cut"></aop:before>
<!--方式二:直接在方法配置里写切入点表达式-->
<aop:before method="before" pointcut="execution(* com.guolian.Spring2.AOP.xml.*.*(..))"></aop:before>
</aop:aspect>
</aop:config>
@Component
public class MyLogger {}
JdbcTemplate
为了使JDBC更易于使用,Spring在 JDBC APL上定义了一个抽象层,以此建立一个JDBC存取框架。
可以将 Sping 的 JdbcTemplate 看作是一个小型的轻量级持久化框架,和之前使用的 DBUtils 很相似
4.环境准备
4.1导入 Jar包
4.1.1IOC容器所需jar包:
commons-logging-1.1.1.jar:
spring-beans-4.0.0.RELEASE.jar beans:配置访问文件、创建和管理bean
spring-context-4.0.0.RELEASE.jar context:Spring的上下文,即IOC容器。
spring-core-4.0.0.RELEASE.jar core:Spring框架基本的核心工具类
spring-expression-4.0.0.RELEASE.jar expression:表达式解析语言
4.1.2JdbcTemplate所需jar包:
spring-jdbc-4.0.0.RELEASE.jar jdbc:封装的jdbc
spring-orm-4.0.0.RELEASE.jar orm:对象关系映射
spring-tx-4.0.0.RELEASE.jar tx:事务
4.1.3数据库驱动和数据源jar包:
druid-1.1.9.jar 数据库连接池
mysql-connector-java-5.1.7-bin.jar mysql 的驱动
4.2增删改查
重点:
在 sql语句中使用通配符:?。
若传过去的是一个字符串,则自动为 这个字符串加 ‘ ’(单引号)。
有两种sql语句不能用通配符赋值:
第一种: where ... in(?)。 (in 里不能用?。in('1,2,3,') 只会执行1)。
若使用通配符,sql中执行的语句就是:DELETE FROM emp WHERE id IN('3,4,5');
#只会删除 id为3的数据
第二种: 模糊查询。模糊查询时,通配符会自动为其加上 单引号。
若使用通配符,sql中执行的语句就是:SELECT id FROM emp WHERE `name` LIKE '%'a'%' ;
#语句直接报错。
xml配置:
<!--引入属性文件-->
<context:property-placeholder location="com/guolian/Spring2/template/jdbc/mysql.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${mysqlDriver}"></property>
<property name="url" value="${mysqlUrl}"></property>
<property name="username" value="${mysqlUsername}"></property>
<property name="password" value="${mysqlPassword}"></property>
</bean>
<!--通过数据源配置 JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
4.2.1update
单条数据增删改
public class TestJdbcTeamplate {
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("com/guolian/Spring2/template/jdbc/jdbc.xml");
//获取配置好的 jdbcTemplate
JdbcTemplate jdbcTeamplate = ac.getBean("jdbcTemplate", JdbcTemplate.class);
@Test
public void testUpdate(){
//直接使用固定的 sql 语句
// jdbcTeamplate.update("INSERT INTO emp VALUES(null,'张三',23,'男')");
//使用预编译
/* String sql = "insert into emp values(null,?,?,?)";
jdbcTeamplate.update(sql,"李四",25,"男");*/
//在in 里不能使用 通配符,否则只会执行第一个id的删除。
String ids = "2,3";
String sql = "delete from emp where id in("+ ids +")";
jdbcTeamplate.update(sql);
}
}
4.2.2batchUpdate
批量增删改
@Test
public void testBatchUpdate(){
String sql = "insert into emp values(null,?,?,?)";
//一个value 就是一个数组。
//所有数组组成一个集合。就是批量操作。
List<Object[]> list = new ArrayList<>();
list.add(new Object[]{"a1",1,"男"});
list.add(new Object[]{"a2",2,"男"});
list.add(new Object[]{"a3",3,"男"});
jdbcTeamplate.batchUpdate(sql,list);
}
4.2.3QueryForObject
查询单个数据或单条数据。
rowMapper:行的映射
@Test
public void testQueryForObject(){
//用来获取单个的值
// jdbcTeamplate.queryForObject(sql,Class);
//用来获取单条数据
// jdbcTeamplate.queryForObject(sql,rowMapper);
String sql = "select id,`name`,age,sex from emp where id=?";
//将字段名与属性名进行映射
RowMapper<Emp> rowMapper = new BeanPropertyRowMapper<>(Emp.class);
Emp emp = jdbcTeamplate.queryForObject(sql, new Object[]{6}, rowMapper);
System.out.println(emp);
//获取单个数据
String sql2 = "select count(*) from emp";
Integer integer = jdbcTeamplate.queryForObject(sql2, Integer.class);
System.out.println(integer);
}
4.2.4Query
查询所有数据。
@Test
public void testQuery(){
String sql = "select id,name,sex,age from emp";
RowMapper<Emp> rowMapper = new BeanPropertyRowMapper<>(Emp.class);
List<Emp> query = jdbcTeamplate.query(sql, rowMapper);
for (Emp emp : query){
System.out.println(emp);
}
}
声明式事务管理
使用原生的JDBC API 实现事务管理,是所有事务管理的基石。
5.1简单配置执行事务
5.1.1XML代码
<context:component-scan base-package="com.guolian.Spring2.book"></context:component-scan>
<!--引入属性文件-->
<context:property-placeholder location="com/guolian/Spring2/book/mysql.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${mysqlDriver}"></property>
<property name="url" value="${mysqlUrl}"></property>
<property name="username" value="${mysqlUsername}"></property>
<property name="password" value="${mysqlPassword}"></property>
</bean>
<!--通过数据源配置 JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--事务是针对于 连接对象 的。由连接对象(Connection) 关闭自动提交、执行手动提交和回滚。-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启注解驱动:扫描并解析与事务有关的注解-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>
5.1.2Java代码
@Transactional :对方法中所有的操作作为一个事务管理
//为当前方法执行事务管理,若方法出错则回滚整个方法。
//若标注在类上,则对整个类有作用。
@Transactional
public void buyBook(String bid, String uid) {
Integer privce = dao.selectPrice(bid);
dao.updatest(bid);
dao.updateBalance(uid,privce);
}
5.2事务
5.2.1@Transactional的属性
若在方法和类上都有 @Transactional ,且它们所定义的属性一样,则就近原则。
若定义的属性不一样,则叠加。
propagation:
**事务的传播级别(传播行为):**<br />
A方法和B方法都有事务,当A在调用B时,会将A中的事务传播给B方法。
@Transactional(propagation = ...)
//Propagation.REQUIRED:必须使用调用者的事务
//Propagation.REQUIRES_NEW:将调用者的事务挂起,使用新的(被调用者)事务进行处理。
isolation:
**事务的隔离级别**:
并发情况下,操作数据的一种规定。
读未提交:脏读、不可重复读、幻读。1
读已提交:不可重复读、幻读。2
可重复读:幻读。4
串行化:线程问题都解决,但效率低下。8
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认隔离级别
不设置时:默认和数据库的隔离级别一样。
MySQL:默认可重复读。
timeout:
**超时**:
在事务强制回滚前,最多可以执行(等待)的时间**。
@Transactional(timeout = 3)//若该方法的执行(等待)时间超过3秒,则回滚整个事务
readOnly:
**只读:**
设置当前事务中的一系列操作是否为只读。
设置为只读之后:Spring会通知MySQL,这个方法是用来读数据的,不用加锁。提高性能。
@Transactional(readOnly = false)//不为只读
@Transactional(readOnly = true)//为只读
rollbackFor :
**设置因为什么异常而回滚**
@Transactional(rollbackFor = {MyException.class,NullPointerException.class})
//设置该方法:因为 MyException(自定义异常)和空指针异常 而回滚
//如果没有遇到这些异常,则不回滚。
norollbackFor :
**设置不因为什么异常而回滚**
@Transactional(noRollbackFor = {MyException.class,NullPointerException.class} )
//与 rollbackFor 相反。
5.2.2使用XML的方式配置事务
不管是使用注解方式,还是使用XML的方式配置事务,都一定要有事务管理器。
思路:将事务管理器看作是一个 切面(将一些方法里的事务横向抽取出来了)。
要配置的事务就是这个切面的通知。(也就是说,我们需要为事务配置切入表达式)
**并且将 事务 全部交给事务管理器来管理**。
<!--配置事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--事务是针对于 连接对象 的。由连接对象(Connection) 关闭自动提交、执行手动提交和回滚。-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务通知,并将通知交给事务管理器来管理。-->
<tx:advice transaction-manager="dataSourceTransactionManager" id="advice">
<tx:attributes>
<!--在设置好的切入点表达式下再次进行事务设置,看哪些方法需要被事务管理-->
<tx:method name="buyBook"/>
<tx:method name="checkout"/>
<!--只有以 select 开头的方法才会被事务处理-->
<tx:method name="select*"/>
<!--将所有方法都管理-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pointcut" expression="execution(* com.guolian.Spring2.book_xml.service.impl.*.*(..))"></aop:pointcut>
<!--为事务通知配置切入点表达式-->
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"></aop:advisor>
</aop:config>