前面已经讲了关于Hibernate和Struts2两个框架的基本内容了,如果不懂的可以看前面的文章哦,很明白从基础逐步进行延伸。下面的话,就用这篇文章来讲解一下关于Spring框架的基本知识。准备好了吗?
1:spring的搭建步骤
一:导包(4+2+1):四个基础包,2个日志包,一个web包
二:创建java对象
三:书写配置注册对象到容器(其中位置任意,建议放在src下,名字也取为applicationContext.xml)
2:spring的概念知识
(1)IOC(Inverse Of Control , 反转控制)知识点
概念:就是反转了对象的创建方式,从我们自己创建反转给程序(spring)
(2)DI(Denpendcy Injection , 依赖注入)
注入方式:set方式注入,构造方法注入,字段注入
注入类型:值类型注入(8大基本类型),引用类型注入(将依赖对象注入)
(3)BeanFactory接口
1)spring原始接口,针对原始接口的实现类功能较为单一;
2)BeanFactory接口实现类的容器特点是每次在获得对象时才创建对象
(4)ApplicationContext
每次容器启动时就会创建容器中配置的所有对象
加载文件的方式:第一:从类路径下加载,ClassPathXmlApplicationContext
第二:从硬盘的绝对路径:FileSystemXmlApplicationContext("d:/xxx/yy/zzz")
其中的(3)和(4)对比中,在web环境中,使用(4),而在资源匮乏的时候,可以使用(3)。
3:Bean元素
(1)基本知识点
(2)scope属性(四种):
类型一:singleton(默认值):单例对象,被标识为单例类型,则在spring容器中,只会存在一个实例
类型二:prototype:多例模型,被标识为多例的对象,每次再获得才会得到对象,每次创建都是新的对象,整合structs2的时候,需要配置ActionBean为多例模式
类型三:request:web环境下,对象与request生命周期一样
类型四:session:web环境下,对象与session生命周期一样
(3)生命周期
1:当scope是单例(默认)的情况时候,就是当spring容器初始化的时候就进行Bean的初始化
2:当scope是prototype的时候,就是当调用getBean()方法的时候才进行Bean的初始化。
4:三种对象创建方式
(1)空参构造(创建一个对象默认会调用空参构造方法)
(2)静态工厂(了解)
(3)实例工厂(了解)
applicationContext配置文件:
上面其中的工厂类如下所示:
package cn.itcast.b_create;
import cn.itcast.bean.User;
public class UserFactory {
public static User createUser(){
System.out.println("静态工厂创建User");
return new User();
}
public User createUser2(){
System.out.println("实例工厂创建User");
return new User();
}
}
5:属性注入(三种方式)
(1)set方法(掌握,最重要)
(2)构造函数(掌握)
(3)p名称空间注入(了解)
(4)spel注入(了解)
tom
jerry
com.jdbc.mysql.Driver
root
1234
其中上面的User类,Car类和CollectionBean类代码如下:
User类:
package cn.itcast.bean;
public class User {
public User() {
System.out.println("User对象空参构造方法!!!!");
}
private String name;
private Integer age;
private Car car;
public User(String name, Car car) {
System.out.println("User(String name, Car car)!!");
this.name = name;
this.car = car;
}
public User(Car car,String name) {
System.out.println("User(Car car,String name)!!");
this.name = name;
this.car = car;
}
public User(Integer name, Car car) {
System.out.println("User(Integer name, Car car)!!");
this.name = name+"";
this.car = car;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public void init(){
System.out.println("我是初始化方法!");
}
public void destory(){
System.out.println("我是销毁方法!");
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
Car类:
package cn.itcast.bean;
public class Car {
private String name;
private String color;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Car [name=" + name + ", color=" + color + "]";
}
}
CollectionBean类(这个主要是用来讲解高级的注入方式)
package cn.itcast.c_injection;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class CollectionBean {
private Object[] arr;//数组类型注入
private List list;//list/set 类型注入
private Map map;//map类型注入
private Properties prop;//properties类型注入
public Object[] getArr() {
return arr;
}
public void setArr(Object[] arr) {
this.arr = arr;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
public Properties getProp() {
return prop;
}
public void setProp(Properties prop) {
this.prop = prop;
}
@Override
public String toString() {
return "CollectionBean [arr=" + Arrays.toString(arr) + ", list=" + list + ", map=" + map + ", prop=" + prop
+ "]";
}
}
测试类的代码,也顺便贴出来,大家可以运行进行测试下:
package cn.itcast.c_injection;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.itcast.bean.User;
public class Demo {
@Test
public void fun1(){
//1 创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
//2 向容器"要"user对象
User u = (User) ac.getBean("user");
//3 打印user对象
System.out.println(u);
}
@Test
public void fun2(){
//1 创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
//2 向容器"要"user对象
User u = (User) ac.getBean("user2");
//3 打印user对象
System.out.println(u);
}
@Test
public void fun3(){
//1 创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
//2 向容器"要"user对象
User u = (User) ac.getBean("user3");
//3 打印user对象
System.out.println(u);
}
@Test
public void fun4(){
//1 创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
//2 向容器"要"user对象
User u = (User) ac.getBean("user4");
//3 打印user对象
System.out.println(u);
}
@Test
public void fun5(){
//1 创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
//2 向容器"要"user对象
CollectionBean cb = (CollectionBean) ac.getBean("cb");
//3 打印user对象
System.out.println(cb);
}
}
PS:这里另外讲一点知识就是关于注解注入的问题。因为在实际开发中,一般都是通过注解来开发的,要不然XML文件就会显得很臃肿,所以就需要有注解的使用。对于注入的话,有两个注解@Resource和@Autowire,它们两者的差别在于:
区别:@Resource中有个属性name(可选),所以首先是会从spring容器中找到对应的名字的装配,如果不存在,然后才会找对应的类型进行注入;而@Autowire,它只会找对应的类型进行装配,进行注入。
6:保持容器中的对象的生命周期的方法
首先在web.xml文件中进行配置一个监听器,以便spring容易中获取对象保持单例
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
classpath:applicationContext.xml
其次,在Action类中进行获取对象(下面是个例子)。
//获得spring容器=>从Application域获得即可
//1 获得servletContext对象
ServletContext sc = ServletActionContext.getServletContext();
//2.从Sc中获得ac容器
WebApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sc);
//3.从容器中获得CustomerService
CustomerService cs = (CustomerService) ac.getBean("customerService"); //这个是需要将对象在spring配置文件中进行配置好的
7:使用注解配置spring
步骤:
一:导包4+2+spring-aop包
二:为主配置文件引入新的命名空间(约束)
三:开启使用注解代理配置文件(在spring的配置文件applicationContext.xml中配置)
四:在类中使用注解完成配置
//@Component("user")
//等价于在applicationContext.xml中配置
public class User {
...........
}
知识点1:将对象注册到容器
//@Component("user") //等价在spring配置文件中设置为:
// @Service("user") // service层
// @Controller("user") // web层
@Repository("user")// dao层
public class User {
.............
}
知识点2:修改对象的作用范围
//指定对象的作用范围
@Scope(scopeName="singleton") //设置为单例 ,设置为prototype,则为多例
public class User {
..........
}
知识点3:值类型注入
public class User {
private String name;
@Value("18") //设置年龄为18
private Integer age;
}
知识点4:引用类型注入
//@Autowired //方法一:自动装配
//问题:如果匹配多个类型一致的对象.将无法选择具体注入哪一个对象.
//@Qualifier("car2")//方法二:使用@Qualifier注解告诉spring容器自动装配哪个名称的对象
@Resource(name="car")//方法三:手动注入,指定注入哪个名称的对象,推荐使用
private Car car;
对应的要在相应的javabean中进行配置的设置
@Component("car") //关键点,配置注解
public class Car {
@Value("玛莎拉蒂")
private String name;
@Value("呕吐绿")
private String color;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Car [name=" + name + ", color=" + color + "]";
}
}
知识点5:初始化和销毁方法
@PostConstruct //在对象被创建后调用.init-method
public void init(){
System.out.println("我是初始化方法!");
}
@PreDestroy //在销毁之前调用.destory-method
public void destory(){
System.out.println("我是销毁方法!");
}
8:spring与junit整合(作用:节省编写测试代码的冗余行数)
步骤:
一:导包4+2+aop+test包
二:配置注释
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
三:进行测试
package cn.itcast.b_test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.itcast.bean.User;
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
//将名为user的对象注入到u变量中
@Resource(name="user")
private User u;
@Test
public void fun1(){
System.out.println(u);
}
@Test
public void fun2(){
System.out.println(u);
}
}
9:Spring中的aop知识点
(1)spring中的aop概念-------简单点说,就是通过spring来实现动态代理对象。
比如具有代表性的例子:1)消除乱码的时候,用的filter
2)动态代理
3)intercepter(拦截器)中
(2)spring实现aop的原理
方法一:动态代理:
缺点:被代理的对象必须要实现接口,才能产生代理对象,如果没有接口将不能使用动态代理技术
方法二:cglib代理
该方法是第三方代理技术,对任何对象生成代理,代理的原理是对代理的对象进行继承代理。如果目标对象被final修饰,那么该类无法被cglib代理(其实这个在Hebinate中的对象的规则中,就有这一点限制)。
PS:在spring中,优先是执行动态代理,如果该类不能通过动态代理来实现,就会通过cglib代理还实现。
另外顺便提一下,关于动态代理到底是怎么回事,下面通过一个代码例子进行显示一下,一般的动态代理是如何实现,了解就好,因为用spring的话就不需要这么麻烦了(注意就是,要实现动态代理的那个类,就一定要实现一个接口,否则无法进行)
接口:(这个就随便写一下,就假设要实现增删改查)
public interface UserService {
void save();
void delete();
void update();
void find();
}
实现类:(比如,每个方法都要打开事务和提交事务,那么就会有重复代码,所以就将打开事务和提交事务通过动态代理来实现)
package cn.itcast.service;
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("保存用户!");
}
@Override
public void delete() {
System.out.println("删除用户!");
}
@Override
public void update() {
System.out.println("更新用户!");
}
@Override
public void find() {
System.out.println("查找用户!");
}
}
动态代理类:
package cn.itcast.c_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import cn.itcast.service.UserService;
import cn.itcast.service.UserServiceImpl;
//观光代码=>动态代理
public class UserServiceProxyFactory implements InvocationHandler {
public UserServiceProxyFactory(UserService us) {
super();
this.us = us;
}
private UserService us;
public UserService getUserServiceProxy(){
//生成动态代理(参数一:类加载对象;参数二:实现类的接口;参数三:实现InvacationHandler接口的类,而我这里就通过该类自身来实现的!)
UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
this);
//返回
return usProxy;
}
@Override
public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
System.out.println("打开事务!");
Object invoke = method.invoke(us, arg2);
System.out.println("提交事务!");
return invoke;
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
cglib的手动实现:(针对上面的代码,只是把动态代理转成该方式来实现)package cn.itcast.c_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import cn.itcast.service.UserService;
import cn.itcast.service.UserServiceImpl;
//观光代码=>cglib代理
public class UserServiceProxyFactory2 implements MethodInterceptor {
public UserService getUserServiceProxy(){
Enhancer en = new Enhancer();//帮我们生成代理对象
en.setSuperclass(UserServiceImpl.class);//设置对谁进行代理
en.setCallback(this);//代理要做什么
UserService us = (UserService) en.create();//创建代理对象
return us;
}
@Override
public Object intercept(Object prxoyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
//打开事务
System.out.println("打开事务!");
//调用原有方法
Object returnValue = methodProxy.invokeSuper(prxoyobj, arg);
//提交事务
System.out.println("提交事务!");
return returnValue;
}
}
测试代码如下:
package cn.itcast.c_proxy;
import org.junit.Test;
import cn.itcast.service.UserService;
import cn.itcast.service.UserServiceImpl;
public class Demo {
@Test
//动态代理
public void fun1(){
UserService us = new UserServiceImpl();
UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
UserService usProxy = factory.getUserServiceProxy();
usProxy.save();
//代理对象与被代理对象实现了相同的接口
//代理对象 与 被代理对象没有继承关系
System.out.println(usProxy instanceof UserServiceImpl );//false
}
@Test
public void fun2(){ //cglib方式
UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
UserService usProxy = factory.getUserServiceProxy();
usProxy.save();
//判断代理对象是否属于被代理对象类型
//代理对象继承了被代理对象=>true 这也就证明了上面提到的消息
System.out.println(usProxy instanceof UserServiceImpl );//true
}
}
(3)aop中的一些名词(结合刚才的实例进行分析,并用不同的颜色进行了标注)
(4)spring中的aop的操作
第一步:导包(4+2):其中的4就是最基础的那几个,前面也有提到
第二步:准备目标对象(简单点说,就是想通过aop操作来进行的事情)
比如上面例子中的该实现类:
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("保存用户!");
//int i = 1/0;
}
@Override
public void delete() {
System.out.println("删除用户!");
}
@Override
public void update() {
System.out.println("更新用户!");
}
@Override
public void find() {
System.out.println("查找用户!");
}
}
第三步:准备通知(编写通知类)
package cn.itcast.e_annotationaop;
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;
//通知类
@Aspect
//表示该类是一个通知类
public class MyAdvice {
@Pointcut("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void pc(){}
//前置通知
//指定该方法是前置通知,并制定切入点
@Before("MyAdvice.pc()")
public void before(){
System.out.println("这是前置通知!!");
}
//后置通知
@AfterReturning("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void afterReturning(){
System.out.println("这是后置通知(如果出现异常不会调用)!!");
}
//环绕通知
@Around("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("这是环绕通知之前的部分!!");
Object proceed = pjp.proceed();//调用目标方法
System.out.println("这是环绕通知之后的部分!!");
return proceed;
}
//异常通知
@AfterThrowing("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void afterException(){
System.out.println("出事啦!出现异常了!!");
}
//后置通知
@After("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void after(){
System.out.println("这是后置通知(出现异常也会调用)!!");
}
}
第四步:配置进行织入,将通知织入目标对象中
1)导入aop约束(这个百度就可以了。很简单,而且这个是spring框架等多种框架都需要知道的)
方法一:通过配置文件来实现
2)编写配置文件(自身习惯这样来编写,免得逻辑代码快过于混乱)
3)测试
package cn.itcast.d_springaop;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.itcast.bean.User;
import cn.itcast.service.UserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:cn/itcast/d_springaop/applicationContext.xml")
public class Demo {
@Resource(name="userService")
private UserService us;
@Test
public void fun1(){
us.save();
}
}
方法二:通过注解实现(了解)
步骤:(1)编写配置文件(其实这个也可以通过注解来实现,具体的看前面知识点有提到)
(2)编写通知类的注解
package cn.itcast.e_annotationaop;
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;
//通知类
@Aspect
//表示该类是一个通知类
public class MyAdvice {
@Pointcut("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void pc(){}
//前置通知
//指定该方法是前置通知,并制定切入点
@Before("MyAdvice.pc()")
public void before(){
System.out.println("这是前置通知!!");
}
//后置通知
@AfterReturning("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void afterReturning(){
System.out.println("这是后置通知(如果出现异常不会调用)!!");
}
//环绕通知
@Around("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("这是环绕通知之前的部分!!");
Object proceed = pjp.proceed();//调用目标方法
System.out.println("这是环绕通知之后的部分!!");
return proceed;
}
//异常通知
@AfterThrowing("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void afterException(){
System.out.println("出事啦!出现异常了!!");
}
//后置通知
@After("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void after(){
System.out.println("这是后置通知(出现异常也会调用)!!");
}
}
AOP实际开发用到的小例子:
(1)统计Action,Service,Dao每个类每个方法的使用次数
需求:在开发过程中,我们有时候需要统计一下执行某个类中的某个方法,它在一个调用过程中,执行了什么其他方法,并且执行每个方法的时间或者次数(用到的都是AOP),从而来进行性能的优化,所以这时候就使用AOP来进行分析就会得到比较好的处理。
(Action,service,dao的方法,我就不写了,自己按照开发模式来写就可以了),下面是AOP代理类的具体代码:
package com.hnu.scw.aop.xml.methodinvocation.count.aspect;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.JoinPoint;
public class MethodInvocationCount {
private Map count = new HashMap();
public void count(JoinPoint joinPoint){
//获取的是目标类
String targetClassName = joinPoint.getTarget().getClass().getSimpleName();
//目标方法
String targetMethodName = joinPoint.getSignature().getName();
String key = targetClassName+":"+targetMethodName;
if(count.containsKey(key)){
Integer value = count.get(key);
count.put(key, ++value);
}else{
count.put(key, 1);
}
System.out.println(targetClassName+"的"+targetMethodName+"方法被调用了"+count.get(key)+"次");
}
}
再写一个测试类吧,这样大体就知道是干了什么
package com.hnu.scw.aop.xml.methodinvocation.count.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima11.spring.aop.xml.methodinvocation.count.action.PersonAction;
import com.itheima11.spring.aop.xml.methodinvocation.count.service.PersonService;
public class MethodInvocationCountTest {
@Test
public void testCount(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
PersonAction personAction = (PersonAction)context.getBean("personAction"); //这里用的是xml配置bean,当然如果用注解也是一样,并没什么影响
personAction.savePerson();
personAction.updatePerson();
PersonService personService = (PersonService)context.getBean("personService");
personService.savePerson();
}
}
(2)权限验证------------------------这个功能在系统中是很重要的吧。实现方法也有很多种,这里就用AOP来实现
步骤的话,主要分为如下几个步骤:步骤:
1、准备service层和dao层的类和接口
2、准备权限类Privilege
3、准备注解@PrivilegeInfo
4、准备注解解析器@PrivilegeInfo(name='')
注解解析器应该把@PrivilegeInfo中的name属性的值解析出来
5、切面
环绕通知
6、spring的配置文件
7、客户端
代码:(service和dao层就不多说了吧,这个随便写个crud都行)
Action类:就是调一下service中的方法,从而来说明权限验证的功能
package com.hnu.scw.spring.aop.xml.privilege.action;
import com.hnu.scw.spring.aop.xml.privilege.service.PersonService;
public class PersonAction {
private PersonService personService;
public void setPersonService(PersonService personService) {
this.personService = personService;
}
public void savePerson(){
Object obj = this.personService.savePerson();//这个获取到调用了方法之后的返回值,在这里如果是error就表示权限验证失败了。
System.out.println(obj);
}
}
权限类:
package com.hnu.scw.spring.aop.xml.privilege.bean;
public class Privilege {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
自定义权限注解:
package com.hnu.scw.spring.aop.xml.privilege.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrivilegeInfo {
String name() default "";
}
注解解析:
package com.hnu.scw.spring.aop.xml.privilege.annotation;
import java.lang.reflect.Method;
public class AnnotationParse {
public static String parse(Class targetClass,String methodName) throws Exception{
//目标方法
Method method = targetClass.getMethod(methodName);
String methodAccess = ""; //方法的权限的名称
//判断目标方法上面是否存在@PrivilegeInfo注解
//@Privilege(name="savePerson")
if(method.isAnnotationPresent(PrivilegeInfo.class)){
PrivilegeInfo privilegeInfo = method.getAnnotation(PrivilegeInfo.class);
methodAccess = privilegeInfo.name();
}
return methodAccess;
}
}
权限验证切面类:
package com.hnu.scw.spring.aop.xml.privilege.aspect;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;
import com.itheima11.spring.aop.xml.privilege.annotation.AnnotationParse;
import com.itheima11.spring.aop.xml.privilege.bean.Privilege;
public class AccessTargetMethod {
private List userPrivilege = new ArrayList();//模拟一下能够验证通过的权限有哪些
public List getUserPrivilege() {
return userPrivilege;
}
public Object accessMethod(ProceedingJoinPoint joinPoint) throws Throwable{
/*
* 得到目标类的class形式
* 得到目标方法
*/
Class targetClass = joinPoint.getTarget().getClass();
String targetMethodName = joinPoint.getSignature().getName();
String methodAccess = AnnotationParse.parse(targetClass, targetMethodName);
boolean flag = false;
for (Privilege privilege: userPrivilege) {
if(methodAccess.equals(privilege.getName())){//放行
flag = true;
break;
}
}
if(flag){//放行
return joinPoint.proceed();
}else{
return "error";
}
}
}
Spring配置文件:
测试类:
package com.hnu.scw.spring.aop.xml.privilege.test;
import java.util.List;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima11.spring.aop.xml.privilege.action.PersonAction;
import com.itheima11.spring.aop.xml.privilege.aspect.AccessTargetMethod;
import com.itheima11.spring.aop.xml.privilege.bean.Privilege;
import com.itheima11.spring.aop.xml.privilege.service.PersonService;
public class PrivilegeTest {
@Test
public void testPrivilege(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
//初始化用户的权限
AccessTargetMethod accessTargetMethod = (AccessTargetMethod)context.getBean("accessMethod");
List privileges = accessTargetMethod.getUserPrivilege();
Privilege privilege = new Privilege();
privilege.setName("savePerson");
privileges.add(privilege);
PersonAction personAction = (PersonAction)context.getBean("personAction");
personAction.savePerson();
}
}
十:spring整合jdbc
概念:spring中提供了一个可以操作数据库的对象(JDBCTemplate JDBC模版对象),对象封装了jdbc技术
在这先提一下,如何用原始的方法进行操作,这个在基础中的连接池都有说过(就是非spring的开发代理模式)
public void fun1() throws Exception{
//0 准备连接池
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///testdata");
dataSource.setUser("root");
dataSource.setPassword("123456");
//1 创建JDBC模板对象
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
//2 书写sql,并执行
String sql = "insert into t_user values(null,'rose') ";
jt.update(sql);
}
好了,上面的内容是不是在基础学习中都有类似的印象呢?确实,其实JdbcTemplate类对象和基础中的QueryRunner是类似的。下面就把上面代码实现的功能通过spring容器来进行演示。
通过spring整合jdbc操作的步骤(用实际例子来进行分析):
(1)导包:4+2+spring-aop包+spring-test包+junit4+JDBC驱动包+c3p0连接池包+spring-jdbc包+spring-tx事务包
(2)编写dao接口
package cn.itcast.a_jdbctemplate;
import java.util.List;
import cn.itcast.bean.User;
public interface UserDao {
//增
void save(User u);
//删
void delete(Integer id);
//改
void update(User u);
//查
User getById(Integer id);
//查
int getTotalCount();
//查
List getAll();
}
(3)编写实现接口类
package cn.itcast.a_jdbctemplate;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import cn.itcast.bean.User;
//使用JDBC模板实现增删改查
public class UserDaoImpl implements UserDao {
private JdbcTemplate js ;
@Override
public void save(User u) {
String sql = "insert into t_user values(null,?) ";
js.update(sql, u.getName());
}
@Override
public void delete(Integer id) {
String sql = "delete from t_user where id = ? ";
js.update(sql,id);
}
@Override
public void update(User u) {
String sql = "update t_user set name = ? where id=? ";
js.update(sql, u.getName(),u.getId());
}
@Override
public User getById(Integer id) {
String sql = "select * from t_user where id = ? ";
return js.queryForObject(sql,new RowMapper(){
@Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
User u = new User();
u.setId(rs.getInt("id"));
u.setName(rs.getString("name"));
return u;
}}, id);
}
@Override
public int getTotalCount() {
String sql = "select count(*) from t_user ";
Integer count = js.queryForObject(sql, Integer.class);
return count;
}
@Override
public List getAll() {
String sql = "select * from t_user ";
List list = js.query(sql, new RowMapper(){
@Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
User u = new User();
u.setId(rs.getInt("id"));
u.setName(rs.getString("name"));
return u;
}});
return list;
}
public void setJs(JdbcTemplate js){this.js = js;}
}
(4)编写配置文件
//数据库名
//驱动类型
//用户数据库名
//密码
(5)进行测试方法类(当然这个在实际开发中,就不需要了,只是方便测试方法是否正确)
package cn.itcast.a_jdbctemplate;
import java.beans.PropertyVetoException;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import cn.itcast.bean.User;
//演示JDBC模板
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource(name="userDao")
private UserDao ud;
@Test
public void fun1() throws Exception{
//0 准备连接池
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///hibernate_32");
dataSource.setUser("root");
dataSource.setPassword("1234");
//1 创建JDBC模板对象
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
//2 书写sql,并执行
String sql = "insert into t_user values(null,'rose') ";
jt.update(sql);
}
@Test
public void fun2() throws Exception{
User u = new User();
u.setName("tom");
ud.save(u);
}
@Test
public void fun3() throws Exception{
User u = new User();
u.setId(2);
u.setName("jack");
ud.update(u);
}
@Test
public void fun4() throws Exception{
ud.delete(2);
}
@Test
public void fun5() throws Exception{
System.out.println(ud.getTotalCount());
}
@Test
public void fun6() throws Exception{
System.out.println(ud.getById(1));
}
@Test
public void fun7() throws Exception{
System.out.println(ud.getAll());
}
}
上面这个例子,相对于实际开发中对数据库的操作基本上已经很齐全了,增删改查都有,如果有什么特别的需求的话,自己按照类似的形式进行编写就可以了,关键是要自己看得懂哪个API是适合什么操作的。。。
好了,下面把在配置文件中读取properties文件的方法也写出来(实际开发中更习惯这一种,因为如果要修改数据库,那么只需要该properties中的内容就可以了,比单独修改配置文件内容方便很多)
properties文件代码(命名为:db.properties):
jdbc.jdbcUrl=jdbc:mysql:///hibernate_32
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=1234
PS:其中上面的jdbc只是用来一个前缀而已,防止与其他的关键字的内容发送冲突,这个可以根据自己的习惯来自由定义即可。
spring配置文件代码(等价上面的配置文件):
十一:spring中的事务知识点
一:基本知识梳理
(1)事务特性:ACID------原子性,一致性,隔离性,持续性
(2)事务并发问题----------1:脏读(一个事务读取到另一个事务未提交的更新数据)不可重复读(在同一事务中,多次读取同一数据返回的结果有所不同)幻读(一个事务读取到另一个事务已提交的insert数据)
(3)事务隔离级别-------1:读未提交 2:读已提交 4:可重复读 8:串行化
(4)spring封装了事务管理代码:打开事务,提交事务,回滚事务
(5)spring提供了一个操作事务的接口:PlatformTransactionMnager接口。针对不同平台会提供不同的实现类。注意:在spring中玩事务管理,最为关键的核心对象就是TransactionManager对象
(6)spring管理事务的属性介绍:事务的隔离级别,是否只读(限制操作是否能修改数据库,true只读,false可操作),事务的传播行为(决定业务方法之间的调用,事务应该如何处理),其中的传播属性有如下所示:
二:搭建事务的环境(直接用代码分析)
Dao层内容:
public interface AccountDao {
//加钱
void increaseMoney(Integer id,Double money);
//减钱
void decreaseMoney(Integer id,Double money);
}
Dao层实现层:
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void increaseMoney(Integer id, Double money) {
getJdbcTemplate().update("update t_account set money = money+? where id = ? ", money,id);
}
@Override
public void decreaseMoney(Integer id, Double money) {
getJdbcTemplate().update("update t_account set money = money-? where id = ? ", money,id);
}
}
Service层接口:
package cn.itcast.service;
public interface AccountService {
//转账方法
void transfer(Integer from,Integer to,Double money);
}
service实现:
package cn.itcast.service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import cn.itcast.dao.AccountDao;
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true)
public class AccountServiceImpl implements AccountService {
private AccountDao ad ;
@Override
public void transfer(final Integer from,final Integer to,final Double money) {
//减钱
ad.decreaseMoney(from, money);
//int i = 1/0;
//加钱
ad.increaseMoney(to, money);
}
public void setAd(AccountDao ad) {
this.ad = ad;
}
}
spring配置文件:
好了,上面就是比较正常的一个开发结构的代码了,但是不包含事务处理的内容,这时候运行的话,是能够正常运行的。但是如果在transfer方法中,出现异常,比如int i = 1/0;那么转账的总数就会出现问题了。
PS:默认的Spring容器中,只对unchecked类型的异常(非编译时异常,如NullPointException,ArryaryOutOfException)进行事务的回滚,而对于checked类型异常(编译时异常,如IOException)是不进行回滚的,但是可以通过进行设置回滚的类型,来进行修改的哦。。所以也就是下面所要讲解的知识点了。
(1)spring管理事务的方式(三种)
方法一:编码式(了解)
spring配置文件:(基于上面的配置文件,添加下面的内容)
service实现层代码:
package cn.itcast.service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import cn.itcast.dao.AccountDao;
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true)
public class AccountServiceImpl implements AccountService {
private AccountDao ad ;
private TransactionTemplate tt;
@Override
public void transfer(final Integer from,final Integer to,final Double money) {
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
//减钱
ad.decreaseMoney(from, money);
int i = 1/0;
//加钱
ad.increaseMoney(to, money);
}
});
}
public void setAd(AccountDao ad) {
this.ad = ad;
}
public void setTt(TransactionTemplate tt) {
this.tt = tt;
}
}
所以,对于事务的处理,spring通过编码式就实现了,对于发生异常情况的处理的回滚操作。
很明显,这种方法碰到多个方法的时候,就需要写多个代码段(execute方法),这样必然显得很麻烦。
方法二:xml配置(aop)(必须掌握)
步骤(1)导包4(基础包)+4(事务包)
(2)配置新的约束(tx)
(3)spring中xml配置通知
(4)spring中xml配置织入
方法三:注解配置(aop)(必须掌握)
步骤:(1)和(2)与spring配置文件方式一样
(3)spring配置文件中开启使用注解管理aop事务
(4)使用注解(在实现类方法中)
package cn.itcast.service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import cn.itcast.dao.AccountDao;
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true)
public class AccountServiceImpl implements AccountService {
private AccountDao ad ;
private TransactionTemplate tt;
@Override
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)
public void transfer(final Integer from,final Integer to,final Double money) {
//减钱
ad.decreaseMoney(from, money);
int i = 1/0;
//加钱
ad.increaseMoney(to, money);
}
public void setAd(AccountDao ad) {
this.ad = ad;
}
public void setTt(TransactionTemplate tt) {
this.tt = tt;
}
}
PS:对于注解方法的使用,可以使用在类的开头,这样就表示类中的方法都使用该注解。。如果想对于某个方法改变注解的形式,那么就可以单独的重新写一个注解。这两种在代码中都有体现。
---------------
十二:Spring的基本开发模式
其实综合前面讲的这些内容,应该够基本的了解Spring的内容了,可能刚开始接触的时候会比较迷茫,感觉不是很懂,会觉得怎么那么多的东需要进行配置,然后还有这么多关联性的内容,但是当使用过后和认真分析过后,就会觉得Spring这个框架确实写得非常非常的好,看源码的时候就会发现里面很多Java的高阶知识,这在我其他的文章也会有提示到的哦。欢迎进行阅读。下面的话,就说一下Spring的一个基本的配置过程好了,这样的话,就有一个配置的基本流程,而不至于不知道从何做起。当然,这只是我个人的开发流程而已,只要适合自己就好了,主要还是要理解。。理解。。。再理解。。。。
(1)配置Bean元素
1:可以采取XML的单独配置
2:可以采取扫描和注解结合的形式进行配置
(2)配置资源扫描------------主要就是对properties文件的扫描,这个的话主要是用于进行数据库的方便
(3)配置需要动态管理的资源-------同样可以用XML或者注解的形式
(4)配置数据源-----可以有JDBC,C3P0。。。等等
(5)配置事务管理
好了呢。。。这些就是基本的spring的知识点啦,,,期待着我持续的更新哟。。还有其他的文章,欢迎大家一起学习交流呢!!!