Spring介绍
Spring概述
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java开发框架,由RodJohnson创建。简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架。
Spring的分层
Web层:---->Spring MVC
Service层:---->Spring IOC/DI
Dao层:----->JdbcTemplete
Spring好处
1.方便解耦,简化开发
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2.AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
3.声明式事务的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
4.方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
5.方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
6.降低Java EE API的使用难度
Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。
7.Java 源码是经典学习范例
Spring的源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。如果想在短时间内迅速提高自己的Java技术水平和应用开发水平,学习和研究Spring源码将会使你收到意想不到的效果。
Spring IOC概念
(Inversion of Control 控制反转) 将对象的创建权反转交给Spring
Spring IOC的原理
工厂+反射+配置文件
Spring IOC入门
Spring maven坐标:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
</dependencies>
Spring下载地址
http://repo.spring.io/release/org/springframework/spring
Spring的目录结构
docs:spring的开发规范以及API
libs:spring架包
scheme:spring约束文件
使用Spring所需要的架包
依赖包:
commons-logging-1.2.jar
log4j-1.2.12.jar
剩下的则是4个核心包
配置Spring核心配置文件(推荐命名:applicationContext.xml)
配置头文件以及约束:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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>
在eclipse中设置
在window->preferences->搜索XML Catalog->点击add找到对应的约束文件位置(Spring包中的Schema文件夹)->在Key type选择Schema location->key 复制applicationContext.xml中头文件的约束地址->apply
将对象的创建权反转交给spring(在配置文件中配置)
<bean id="" class=""></bean>
例:一个Spring的入门程序
@Test
public void saveUser_() {
//1.创建 Spring工厂对象
/*
* ApplicationContext 是接口:
* 创建工厂对象的时候是通过new 子类
*/加载配置文件时,创建工厂对象,bean生命周期开始
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从spring工厂对象中获取对象
IUserService userServcie = (IUserService) ac.getBean("userService");
userServcie.saveUSer();
//关闭工厂,bean生命周期结束
ac.close();
}
Spring工厂类
BeanFactory-------->老版本的工厂类
ApplicationContext---------->新版本的工厂类(继承于BeanFactory)
BeanFactory与ApplicationContext区别:
BeanFactory:当调用getBean方法的时候,才会去创建对象
ApplicationContext:当加载spring核心配置文件的时候,就会创建对象
ApplicationContext的2个子类
ClassPathXmlApplicationContext: 从类加载路径中加载配置文件
FileSystemXmlApplicationContext: 从文件系统中加载配置文件(基本很少使用)
Spring配置文件(核心)
bean配置
id:使用了约束中的唯一约束 不能存在id同名的情况
name:可以不唯一 但是项目中建议不要重复
bean的生命周期
<bean id="userService" class="com.igeekhome.spring.UserService" init-method="initService" destroy-method="destroyService"></bean>
bean的范围(掌握)
scope属性值:(singleton、peototype常用)
singleton: 默认是 单例模式 每次调用getBean()返回的对象都是同一个
prototype: 多例
request: 在web中有效,spring创建对象之后,会在request域对象中放一份
session: 在web中有效,spring创建对象之后,会在session域对象中放一份
Spring中Bean的管理
Spring中Bean的实例化操作
- 根据在bean中设置的class属性值,通过反射,默认调用了无参构造函数
<bean id="userService1" class="com.igeekhome.spring.UserService" scope="prototype"></bean>
- 静态工厂实例化对象
//因为是静态方法,可以直接通过类名调用工厂方法,所以在此处可以直接设置factory-method方法
<bean id="useDaoFactory" class="com.igeekhome.spring02.UserDaoFactory" factory-method="getUserDao"></bean>
- 非静态工厂实例化对象
<!-- 因为工厂类中是非静态方法,调用方法需要先产生工厂对象,所以分2步配置 -->
<bean id="useDaoFactory" class="com.igeekhome.spring.UserDaoFactory"></bean>
<!-- 配置工厂所产生的对象 factory-bean指定工厂的id或者name factory-method:工厂方法名 -->
<bean id="ud" factory-bean="useDaoFactory" factory-method="getUseDao_"></bean>
Spring DI:依赖注入 (Dependency Injection)
IOC与DI的区别
IOC:控制反转,将对象的创建权反转给Spring进行管理(主要解决的问题是:对象的创建)
DI:在IOC的前提下,Spring在管理对象的时候,可以将对象所依赖的属性进行注入(主要解决的问题是:对象之间的依赖关系)
Spring中Bean的属性注入(DI)
- 构造方法注入(构造注入)注意:需要提供对应的构造方法
<bean id="car" class="com.igeekhome.spring.Car">
基本类型 name:属性名称 value:属性值
<constructor-arg name="name" value="奥拓"></constructor-arg>
<constructor-arg name="price" value="60000"></constructor-arg>
</bean>
<bean id="employee" class="com.igeekhome.spring.Employee">
对象类型 name:属性名称 ref:引用bean的id或者name
<constructor-arg name="car" ref="car"></constructor-arg>
</bean>
- Set方法注入(Set注入)注意:要提供无参构造函数 并且提供set方法
<bean id="car" class="com.igeekhome.spring.Car">
<property name="name" value="奥拓"></property>
<property name="price" value="100000"></property>
</bean>
<bean id="employee" class="com.igeekhome.spring.Employee">
<property name="car" ref="car"></property>
</bean>
- p命名空间注入(Spring 2.5之后支持的)
首先,需要引入p命名空间,在头文件中加入:xmlns:p="http://www.springframework.org/schema/p"然后通过p命名空间进行属性注入。
对于基本类型 p:变量名 = "值"
对于对象类型 p:变量名-ref = "bean的id或者name"
<bean id="car" class="com.igeekhome.spring.Car" p:name="奥迪" p:price="200000"></bean>
<bean id="employee" class="com.igeekhome.spring.Employee" p:car-ref="car"></bean>
- SpEL注入:Spring Expression Language Spring表达式语言(Spring 3.0之后支持) 语法:#{SpEL}
//这个其实,你需要在类中设置了实际的实体参数,用的不多
<bean id="carInfo" class="com.igeekhome.spring.CarInfo">
</bean>
<bean id="car" class="com.igeekhome.spring.Car">
<property name="name" value="#{carInfo.name}"></property>
<property name="price" value="#{carInfo.getPrice()}"></property>
</bean>
<bean id="employee" class="com.igeekhome.spring.Employee">
<property name="car" value="#{car}"></property>
</bean>
5 .Spring Bean中注入集合类型(了解)
//在这里,采用的是set注入的方式
<bean id="collectionDemo" class="com.igeekhome.spring04.CollectionDemo">
//String数组
<property name="arr">
<list>
<value>张三</value>
<value>李四</value>
<value>王五</value>
<value>赵六</value>
</list>
</property>
//list集合
<property name="lists">
<list>
<value>一块钱</value>
<value>两块钱</value>
<value>三块钱</value>
<value>四块钱</value>
</list>
</property>
//set集合(Set集合的基本特征是不记录添加顺序,不允许元素重复,常用的是hashset)
<property name="sets">
<set>
<value>昨天</value>
<value>今天</value>
<value>明天</value>
</set>
</property>
//map集合
<property name="map">
<map>
//键值对
<entry key="昨天" value="吃肉喝酒"></entry>
<entry key="今天" value="上课睡觉"></entry>
<entry key="明天" value="啥都不会了"></entry>
</map>
</property>
</bean>
多个模块整合(多个spring配置文件)
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
<!-- 加载其他spring配置文件 路径配置一般不用"."("."一般用于完整类名) 使用/ -->
<!-- 如果配置文件位于某一个包中 -->
<import resource="com/igeekhome/spring04/applicationContext2.xml"/>
<!-- 如果同样处于src中 <import resource="applicationContext2.xml"/> -->
Spring与Web应用的整合
- 为了避免在Servlet中频繁的创建工厂,浪费服务器资源。一般来说,一个项目只要一个Spring工厂就可。
在服务器启动的时候,创建工厂存放在ServletContext当中。当要使用的时候,从ServletContext中获取即可。 - 这个时候,就需要监听ServletContext的创建与销毁,Spring提供了这样的方法:ContextLoaderListener 监听器,只需要注册即可。
- 此时需要导入一个web包:spring-web-4.3.12.RELEASE.jar
- 注意:ContextLoaderListener中有contextConfigLocation属性,是配置文件的路径,
- 因为默认路径是/WEB-INF/applicationContext.xml
- 而我们项目的配置路径在src中(类加载路径中)所以我们需要在web.xml中配置全局参数,指定contextConfigLocation 为当前项目的spring配置文件路径
<context-param>
<param-name>contextConfigLocation</param-name>
//设置类加载路径
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
- 这样,在需要使用Spring工厂的时候,从Servlet中获取, 不过需要依靠WebApplicationContextUtils工具类
WebApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
Spring IOC基于注解开发
此时,在原有的6个包的基础上,增加了一个aop的包,现在的目录结构为:
- 创建Spring配置文件,引入约束以及约束文件,需要额外引入 context命名空间以及约束文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
...
</beans>
- 创建接口及实现类
- 开启注解扫描
<!-- 开启注解扫描 base-package:需要扫描的包 即哪些包需要使用Spring IOC注解 -->
<context:component-scan base-package="com.igeekhome.demo02"></context:component-scan>
<!-- 让注入注解生效(xml和注解整合使用的时候开启)-->
<context:annotation-config></context:annotation-config>
注:Spring 开启Annotation <context:annotation-config> 和 <context:component-scan>诠释及区别
<context:annotation-config> 和 <context:component-scan>的区别
<context:annotation-config> 是用于**那些已经在spring容器里注册过的bean(无论是通过xml的方式还是通过package sanning的方式)上面的注解。
<context:component-scan>除了具有<context:annotation-config>的功能之外,<context:component-scan>还可以在指定的package下扫描以及注册javabean 。
- 在类上添加注解
@Component("userDao") //相当于<bean id="userDao" class="com.igeekhome.demo01.UserDao" ></bean>
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("UserDao...保存用户...");
}
}
- 一个测试类方法
public class Demo {
@Test
public void testSave() {
//1.创建Spring工厂
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从Spring工厂/Spring IOC容器 中获取 UserDao实例
//下面是2种方法,一种是为其指定类型无需强制类型转化,一种需要装换
//IUserDao userDao = (IUserDao) ac.getBean("userDao");
IUserDao userDao = ac.getBean("userDao", UserDao.class);
userDao.save();
System.out.println(userDao);
}
}
Spring IOC的注解详解
我们都知道,添加注解的组件是@Component,为了比较直观的区分MVC的各个层,@Component又产生了3个衍生注解来进行区分,它们的分工为:
- @Controller----->控制层
- @Service----->业务层
- @Repository----->持久层
Spring IOC与生命周期有关的注解
//配置init-method(初始化方法)
@PostConstruct
public void initMethod() {
System.out.println("userDao初始化...");
}
//配置 destroy-method(销毁方法)
@PreDestroy
public void destroyMethod() {
System.out.println("userDao销毁...");
}
Spring IOC作用范围scope
scope注解是写在class上的
//@Scope("prototype")
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("UserDao...保存用户...");
}
...
}
scope有4个属性:后2种使用不多
singleton:默认单例
prototype:多例
request
session
基于注解的属性的注入,(可以没有set方法)
对于基本类型,如果没有提供set方法,那么@Value注解添加在属性上面:
@Value("张三")
private String name;
如果提供了set方法,那么@Value注解建议添加在set方法上(实际上,无论有没有set方法,注解都可以加在属性上,不过编程规范,建议加在set方法上)
@Value("张三")
public void setName(String name) {
this.name = name;
}
对于对象类型
注:IOC容器:IOC容器是加载spring配置文件后,维护的所有Bean交给Spring进行管理
(1)@Autowired :按照类型装配(在Spring IOC容器中查找是否存在相同类型的Bean 如果有就装配, 但是不能同时在IOC容器中出现2个及其以上的相同类型的Bean)
(2)但是一般来说,我们都是按照名称装配 ,即@Autowired 要与 @Qualifier结合使用
@Autowired
@Qualifier("userDao")
private IUserDao userDao;
(3)关于@Resource注解
如果注解中包含name:根据进行名称进行装配,如果没找到。报没有Bean异常(NoSuchBean…) (推荐)
如果注解中没有name,分以下2种情况分析:
- 如果注解是加在字段上,先根据字段名进行名称装配,如果根据字段名装配不成功,再根据类型进行装配(如果根据类型装配,那么Spring IOC容器中相同类型的Bean只能有一个 否则报不唯一异常)
下面呢,是一个例子帮助理解:
首先,字段名设置为userDao
@Resource(name="userDao1111") Spring中有一个对象(userDao1111)------>(根据名称匹配,找到userDao1111,成功)
@Resource(name="userDao1111") Spring中有两个对象(userDao1111 userDao2222 )----->(根据名称匹配,找到userDao1111,成功)
@Resource----->Spring中有两个对象(userDao ,userDao2222)----->(没有name属性,根据类型匹配,找到userDao,成功)
@Resource----->Spring中有两个对象(userDao1111 ,userDao2222)----->(没有name属性,根据类型匹配,相同类型超过一个,失败)
@Resource----->Spring中有一个对象(userDao1111)------> (没有name属性,根据类型匹配,成功)
- 如果注解是加在set方法上,先根据属性名称进行名称装配,如果根据属性名装配不成功,再根据类型进行装配(如果根据类型装配,那么Spring IOC容器中相同类型的Bean只能有一个 否则报不唯一异常)原理跟上方差不多。
关于对基于xml配置以及基于注解的配置总结
XML:任意的Bean都可以配置在xml中,结构清晰
注解:开发便捷 别人的框架 不能使用注解开发(JDBCTemplete)
所以可以对二者进行整合使用:
对于bean的配置,在xml中进行配置(IOC)
将对象的依赖,使用注解配置 (DI),在那个时候,xml中不需要开始注解扫描了,只需要让注册的注解生效即可,即 <context:annotation-config></context:annotation-config>
Spring基于XML的AOP开发
AOP概述:在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP:面向切面编程,是OOP的延伸、拓展。主要用于解决OOP开发中所遇到的问题。
Spring的AOP的底层原理
动态代理:
- JDK代理:只能为实现了接口的类生成代理
- Cglib代理(第三方):为没有实现接口的类生成代理(子类代理)
Spring的AOP的底层实现(了解)
- JDK代理(接口代理)
- Cglib代理:CGLIB(Code Generation Library)是一个开源项目! 是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
不想总结了,其实呢都在视频里,这部分的内容也是了解,不是掌握,详情看2017-10-24_05_Spring AOP底层实现和06的的视频,我有时间再整理吧 啊啊啊 要疯了!!!
Spring AOP基于XML的开发(AspectJ基于XML的实现)
AOP思想由AOP联盟提出来的,Spring只是AOP思想应用得最佳的一个框架
Spring提供了自己的AOP开发(比较繁琐)。AspectJ是一个AOP框架,Spring将AspectJ作为自身AOP的实现。
Spring实现AOP开发:
(1)Spring自身的传统的AOP开发 (弃用)
(2)Spring基于AspectJ的AOP开发(开发中常用) AOP中专业术语:
- 连接点(JoinPoint)
- 切入点(Pointcut)
- 引介(Introduction)
- 增强、通知(Advice)
- 目标对象(Target)
- 织入(Weaving)
- 代理(Proxy)
- 切面(Aspect) 下面,是一张图,可以看看*__*
那,第一步还是先导入架包,下面是开发所依赖的包:
第二步,就是创建配置文件,引入命名空间(aop)
下面,是头文件信息:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
...
</beans>
创建类与接口
那,为了避免每次测试都要创建一个工厂,我们可以将Spring与Junit整合,这个时候呢,就需要导入一个架包啦,它的名字叫做spring-test-4.3.12.RELEASE.jar。
//Junit4与Spring整合
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource(name="userDao")
private IUserDao userDao;
@Test
public void testUserDao() {
//直接,就可以调用方法啦
userDao.addUser();
userDao.deleteUser();
userDao.updateUser();
userDao.selectUser();
}
}
然后创建一个切面(包含增强)
将切面类交给Spring IOC容器进行管理
配置切入点
下面,是一个小小的例子呢:
开启Spring IOC注解扫描
<context:component-scan base-package="com.igeekhome.springaop"></context:component-scan>
<!-- 将类交给Spring IOC进行管理 -->
<bean id="myaspect" class="com.igeekhome.springaop.MyAspect"></bean>
<!-- 准备生成目标对象的代理类对象 -->
<aop:config>
<!--配置切入点:指定拦截哪些方法
expression:切入点表达式
格式:execution(访问修饰符(可选) 返回值 包名.类名.方法名(参数)) //..代表任意参数(可选)
id:是给切入点设置id-->
<aop:pointcut expression="execution(* com.igeekhome.springaop.UserDao.addUser(..))" id="ad"/>
<aop:pointcut expression="execution(* com.igeekhome.springaop.UserDao.deleteUser(..))" id="dt"/>
<aop:pointcut expression="execution(* com.igeekhome.springaop.UserDao.updateUser(..))" id="up"/>
<aop:pointcut expression="execution(* com.igeekhome.springaop.UserDao.selectUser(..))" id="sel"/>
<aop:pointcut expression="execution(* com.igeekhome.springaop.UserDao.exUser(..))" id="ex"/>
<!-- 配置切面 ref:让哪个bean作为切面 id:给切面设置id-->
<aop:aspect ref="myaspect" id="mas">
<!-- 增强处理
aop:before:增强(通知)类型中的一种 前置增强(通知) 在目标对象代码执行之前执行增强代码
pointcut-ref:切入点的id (向哪个切入点织入增强)
method:向切入点织入哪个增强
将方法名为 checkPrvi 的增强 织入到 id为“ad”的切入点
-->
<aop:before pointcut-ref="ad" method="checkPrvi"/>
<!-- 后置通知 在目标对象代码执行之后执行增强代码 returning:可以获取目标对象的返回值-->
<aop:after-returning method="addLog" pointcut-ref="dt" returning="result"/>
<!-- 配置环绕通知 在目标对象代码执行前后都会执行增强代码-->
<aop:around method="around" pointcut-ref="up"/>
<!-- 异常抛出通知 在目标对象代码执行执行时发生异常时执行的增强代码-->
<aop:after-throwing method="ex" pointcut-ref="sel" throwing="e"/>
<!-- 最终通知" 无论发不发生异常,都会执行的增强-->
<aop:after method="after" pointcut-ref="ex"/>
</aop:aspect>
</aop:config>
下面是上面配置的那个切面类啊
package com.igeekhome.springaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//切面类
public class MyAspect {
//增强
//...
//检查权限
public void checkPrvi(JoinPoint jp) {
System.out.println("权限检查..."+jp);//连接点对象
}
//日志记录
public void addLog(Object result) {
//获取 目标对象的返回值
System.out.println("新增日志记录----"+result);
}
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("记录开始时间");
//需要手动放行目标对象方法
pjp.proceed();
System.out.println("记录结束时间");
}
public void ex(Throwable e) {
System.out.println("发现异常..."+e);
}
public void after() {
System.out.println("最终通知执行了...");
}
}
解析一下:
开启Spring IOC注解扫描:
<context:component-scan base-package="com.igeekhome.springaop"></context:component-scan>
expression:切入点表达式
配置切入点:指定拦截哪些方法 格式:execution:访问修饰符(可选) 返回值(如果是*,代表任意的返回值) 包名.类名.方法名(参数))
注:..代表任意参数(可选) id:是给切入点设置id
<aop:pointcut expression="execution(* com.igeekhome.springaop.UserDao.addUser(..))" id="ad"/>
配置切面
ref:让哪个bean作为切面
id:给切面设置id(这个id设置不设置的无所谓)
<aop:aspect ref="myaspect" id="mas">
增强处理
aop:before:增强(通知)类型中的一种,前置增强(通知),在目标对象代码执行之前执行增强代码
pointcut-ref:切入点的id,(向哪个切入点织入增强)
method:向切入点织入哪个增强
比如:将方法名为 checkPrvi 的增强,织入到 id为“ad”的切入点
<aop:before pointcut-ref="ad" method="checkPrvi"/>
Spring AOP基于注解的开发
Spring 基于注解的Aspectj开发
1.恩 架包图:
2.创建spring配置文件,导入aop命名空间
<!-- 开启Spring IOC注解扫描 -->
<context:component-scan base-package="com.igeekhome.springaop"></context:component-scan>
<!-- 开启aop自动代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 将类交给Spring IOC进行管理 -->
<bean id="myaspect" class="com.igeekhome.springaop.MyAspect"></bean>
3.创建类与接口
4.创建切面类
5.配置切面类(告知spring那个类是切面类)
//告知Spring MyAspect 为切面类
@Aspect
public class MyAspect {
//前置通知
@Before("execution(* com.igeekhome.springaop.UserDao.addUser(..))")
public void checkPrvi(JoinPoint jp) {
System.out.println("权限检查..."+jp);//连接点对象
}
}
6.在切面类中创建增强、通知
7.在通知上通过注解指定切入点以及切入点表达式
关于spring aop中切入点的配置(其实就是对方法的抽取)
首先就是要把方法进行声明
@Pointcut("execution(* com.igeekhome.springaop.UserDao.addUser(..))")
private void pointcut_1() {}
然后在通知类型中指定切入点
格式:类名.方法名()
@Before("MyAspect.pointcut_1()")
public void checkPrvi(JoinPoint jp) {
System.out.println("权限检查..."+jp);//连接点对象
}
这样就抽取出来啦。好处是避免在通知类型中重复修改切入点 。现在只需要直接修改切入点声明即可
下面是例子呀:
package com.igeekhome.springaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//切面类
//告知Spring MyAspect 为切面类
@Aspect
public class MyAspect {
//前置通知
@Before("MyAspect.pointcut_1()")
public void checkPrvi(JoinPoint jp) {
System.out.println("权限检查..."+jp);//连接点对象
}
//日志记录
//后置通知
@AfterReturning(value="MyAspect.pointcut_2()",returning="result")
public void addLog(Object result) {
//获取 目标对象的返回值
System.out.println("新增日志记录----"+result);
}
//环绕通知
@Around("MyAspect.pointcut_3()")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("记录开始时间");
//需要手动放行目标对象方法
pjp.proceed();
System.out.println("记录结束时间");
}
//异常抛出通知
@AfterThrowing(value="MyAspect.pointcut_4()",throwing="e")
public void ex(Throwable e) {
System.out.println("发现异常..."+e);
}
//最终通知
@After("MyAspect.pointcut_5()")
public void after() {
System.out.println("最终通知执行了...");
}
//声明切入点
@Pointcut("execution(* com.igeekhome.springaop.UserDao.addUser(..))")
private void pointcut_1() {}
@Pointcut("execution(* com.igeekhome.springaop.UserDao.deleteUser(..))")
private void pointcut_2() {}
@Pointcut("execution(* com.igeekhome.springaop.UserDao.updateUser(..))")
public void pointcut_3() {}
@Pointcut("execution(* com.igeekhome.springaop.UserDao.selectUser(..))")
public void pointcut_4() {}
@Pointcut("execution(* com.igeekhome.springaop.UserDao.exUser(..))")
public void pointcut_5() {}
}
Spring jDBC模板类(JDBCTemplate)
Spring是一个EE的一站式开发框架,对每一层都提供了支持,持久层即为--->JDBCTemplate
1. 创建项目,导入架包
2.创建spring配置文件
3.要想与数据库进行连接,首先就需要一个数据源(连接池),其实Spring也提供了自身的数据源:DriverManagerDataSource这样的一个类,当然使用者不多,我们还是主要使用druid
他们在Spring ioc容器中中可以进行配置
<!-- Spring配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/igeekhome"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- Druid 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/igeekhome"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<property name="minIdle" value="20"></property>
<property name="initialSize" value="30"></property>
<property name="maxActive" value="50"></property>
</bean>
4.已上两者都是可以的,然后再进行jdbc模板的配置
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
5.这样,在测试类中进行数据库的连接时,只需要一步操作,简化了许多:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Test
public void insertUser() {
jdbcTemplate.update("insert into t_user(name,password) values(?,?)","李四","88888");
}
6.当然,其实在实际开发中,数据源一般配置在专门的文件中:db.properties
# mysql
mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/igeekhome
mysql.username=root
mysql.password=root
mysql.minIdle=10
mysql.initialSize=20
mysql.maxActive=50
7.需要在Spring ioc容器中导入:
<!-- 引入属性配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
8.这样,数据源的设置如下:(与jstl语法相近)
<!-- Druid 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${mysql.driverClassName}"></property>
<property name="url" value="${mysql.url}"></property>
<property name="username" value="${mysql.username}"></property>
<property name="password" value="${mysql.password}"></property>
<property name="minIdle" value="${mysql.minIdle}"></property>
<property name="initialSize" value="${mysql.initialSize}"></property>
<property name="maxActive" value="${mysql.maxActive}"></property>
</bean>
9.配置好,就是实现完整的增删改查啦,其实增删改都挺简单的,查不简单,下面是增删改的例子,查要分开来说:
//新增
@Test
public void insertUser() {
jdbcTemplate.update("insert into t_user(name,password) values(?,?)","李四","88888");
}
//更新
@Test
public void update() {
jdbcTemplate.update("update t_user set name=?,password=? where id = ?","铁蛋儿","676767",7);
}
//删除
@Test
public void delete() {
jdbcTemplate.update("delete from t_user where id = ?",6);
}
10.查询语句
(1)查询单个字段:根据id查询姓名
@Test
public void searchNameById() {
//参数:1.sql语句 2.查询类型 3.参数
String name = jdbcTemplate.queryForObject("select name from t_user where id = ?",String.class, 1);
System.out.println(name);
}
@Test
public void getCount() {
//查询记录的条数
//select count(*) from t_user
int count = jdbcTemplate.queryForObject("select count(*) from t_user",Integer.class);
System.out.println(count);
}
(2)查询并封装成对象:根据id进行查询
@Test
public void getUser() {
//查询单个对象
List<User> user = jdbcTemplate.query("select * from t_user where id = ?", new MyRowMapper(), 1);
System.out.println(user);
}
@Test
public void getUsers() {
//查询多个对象
List<User> users = jdbcTemplate.query("select * from t_user", new MyRowMapper());
System.out.println(users);
}
关于其中的参数:new MyRowMapper(),其实是实现了一个RowMapper的一个子类,我其实看不太懂
/*
* 类似类型转换器 每次查询一条记录 都会调用mapRow方法 (在方法内部实现将字段封装成实体)
*
* RowMapper:只是告诉它转换规则(如何封装对象)
*/
class MyRowMapper implements RowMapper<User>{
@Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
// TODO Auto-generated method stub
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("name"));
user.setPassword(rs.getString("password"));
return user;
}
}
最后贴出完整的代码:
package com.igeekhome.springjdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Test
public void insertUser() {
jdbcTemplate.update("insert into t_user(name,password) values(?,?)","李四","88888");
}
//插入
@Test
public void insertUser_druid() {
jdbcTemplate.update("insert into t_user(name,password) values(?,?)","赵六","88888");
}
//更新
@Test
public void update() {
jdbcTemplate.update("update t_user set name=?,password=? where id = ?","铁蛋儿","676767",7);
}
//删除
@Test
public void delete() {
jdbcTemplate.update("delete from t_user where id = ?",6);
}
//查询
//1.查询单个字段
//根据id查询姓名
@Test
public void searchNameById() {
//参数:1.sql语句 2.查询类型 3.参数
String name = jdbcTemplate.queryForObject("select name from t_user where id = ?",String.class, 1);
System.out.println(name);
}
@Test
public void getCount() {
//select count(*) from t_user
int count = jdbcTemplate.queryForObject("select count(*) from t_user",Integer.class);
System.out.println(count);
}
//查询 封装成对象
//根据id查询对象
@Test
public void getUser() {
List<User> users = jdbcTemplate.query("select * from t_user where id = ?", new MyRowMapper(), 1);
System.out.println(users);
}
@Test
public void getUsers() {
List<User> users = jdbcTemplate.query("select * from t_user", new MyRowMapper());
System.out.println(users);
}
}
/*
* 类似类型转换器 每次查询一条记录 都会调用mapRow方法 (在方法内部实现将字段封装成实体)
*
* RowMapper:只是告诉它转换规则(如何封装对象)
*/
class MyRowMapper implements RowMapper<User>{
@Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
// TODO Auto-generated method stub
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("name"));
user.setPassword(rs.getString("password"));
return user;
}
}
#ENDDING!
上一篇: vue之登录注册
下一篇: 【LeetCode】链表——旋转链表