赞
踩
spring重要性
在当前的系统中,spring的重要性and占比性高达50%,无论是在ssm、ssh等框架中,spring始终屹立在前方,所以对于一个程序员来说,spring技术是每一个程序员必备的技术。好了,那我们就开始学习spring了吧~
···········
在java程序中创建对象是必然的,只要需要对象就会去创建一个对象,但是呢创建后就没有销毁等操作了,这就意味着对象和内存就得不到更好的管理,虽然有垃圾回收机制,但是至于什么时候回收这也是jvm的事情,所以也不好判断,为了解决对象和内存的问题,spring就来的刚刚好。
Spring概念
spring可以看作是一个容器,容器里面装了很多的对象(bean)并且维护起来,要使用对象的时候,直接在容器里面取就可以了
Spring作用
Spring最主要的作用就是管理的对象的完整生命周期(对象的创建、使用、销毁等)包括对象之间的依赖关系给,spring都可以去管理
在maven项目中,要使用某一个框架呀啥的都是需要去导入对应的jar包的
依赖:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
当然哈,在使用配置文件进行操作的时候,实体类吖、dao层啊、service层的代码的连接自己要写好哟,spring的实现都是在这些基础上的哈。
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--先放在这里,先去完成controller、serviceImpl里面的对象创建再来填补这里代码-->
注意:每一个xml配置文件,包括mybatis的config.xml吖、mapper.xml啊这些的xml文件,都会是有一个头的,这个头相当于就是告诉我们的编译器,这个xml文件是一个什么类型的xml文件
private PetService petService = new PetServiceImpl();
<?xml version="1.0" encoding="UTF-8"?> <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"> <!-- 这里面就是放每一个对象啦(bean),每一个bean就是每一个对象,根据id去判断具体是那个类、接口的对象,然后通过反射就会去创建对应id的对象--> <bean id="petDao" class="com.xiaowang.dao.impl.PetDaoImpl"> <!-- 获取dao层的对象,完成对数据库信息的管理--> </bean> <bean id="petService" class="com.xiaowang.service.impl.PetServiceImpl"> <!-- 获取service层的对象,去调用dao层内对象,所以需要去与dao层进行关联--> <!-- property标签内的name就是跟对象创建set的方法后面的方法名,也就是方法名去掉set的部分, 比如:setPetDao——>petDao setPetService——>petService 然后ref里面的就是要连接容器中对象的id,--> <property name="petDao" ref="petDao"/> </bean> <bean id="petController" class="com.xiaowang.controller.PetController"> <property name="petService" ref="petService"/> </bean> <!-- 相当于现在就在容器中放了三个对象了,三个对象的关系也由ref给连接起来了--> </beans>
package com.xiaowang; import static org.junit.Assert.assertTrue; import com.xiaowang.controller.PetController; import com.xiaowang.entity.Pet; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Unit test for simple App. */ public class AppTest { /** * Rigorous Test :-) */ @Test public void shouldAnswerWithTrue() { //创建容器对象,容器就是刚刚配置的xml文件 ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml"); //从容器中获取对象 //getBean里面的name就是xml文件里面controller的id,根据这个id和后面的类类型,这样就可以拿到容器里面对应的对象 PetController petController = classPathXmlApplicationContext.getBean("petController", PetController.class); //因为在xml配置文件中,controller、service、dao的对象关系已经连接了,所以也不用自己去实例化了 Pet xiaowang = petController.add("xiaowang", "123456"); System.out.println(xiaowang); } }
结果:
id 表示bean在容器中的唯一标识 一般是类名首字母小写 如果没有写 默认值为 全路径#第几个 com.zlt.controller.UserController#0
class 类的全路径
init-method 初始化方法 对象创建后会被调用一次
destroy-method 销毁方法 容器销毁之前会被调用一次
scope="singleton" 作用范围
singleton 单例模式 默认值
prototype 工厂模式 设置为工厂模式的时候销毁方法不会执行
如果是在web项目中 request 请求作用域 session 会话作用域 globalSession 全局作用域
autowire 自动装配
byType 默认从容器中寻找对应的类型的bean注入进去 如果匹配到多个就会不知道注入谁进去
byName 根据id和属性名进行自动注入
constructor 通过构造来进行注入
no 和 default 都相当于不自动注入
lazy-init 懒加载 设置为true的时候容器创建时不会去初始化对象 默认false
在一般的情况下,使用注解完成项目是更方便的,但是注解的实现一般都是自己的类才能去用注解完成,要是要使用外部的资源的话,就要用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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--进行注解的配置-->
</beans>
<!-- 开启注解:注解是需要开启的-->
<context:annotation-config></context:annotation-config>
<!-- 扫描注解:扫描注解的话,是辉自动开启注解的,所以上面开启注解语句就可以不要了-->
<context:component-scan base-package="com.xiaowang">
<!-- 一般扫描的注解直接在包上就行了,这样的话,这个包下面的所有注解都可以被扫描了,不然要一个一个路径地去扫描-->
</context:component-scan>
注解设置(各类)
将需要放入spring容器的类加上对应的注解,将其放入spring容器中,然后就可以用了,如图:
注解解释:
@Controller 一般写在类上 标记当前是控制器 将当前类的对象放入容器中 id默认是类名首字母小写
@Service 一般写在类上 标记当前是业务层 将当前类的对象放入容器中 id默认是类名首字母小写
@Repository 一般写在类上 标记当前是持久层 将当前类的对象放入容器中 id默认是类名首字母小写
@Component 一般写在类上 不标记是哪一层 将当前类的对象放入容器中 id默认是类名首字母小写
注入值
当spring容器中有了类的对象后,但是这些类对象的值是并没有设置的,所以我们需要去通过注解注入值的方式去完成赋值。并且虽然现在各类注解写了,但是还没有关联,所以可以通过注解去完成注解连接。
注入解释:
@Autowired 自动装配 默认从容器中获取bean并且自动注入到标记的内容上 可以标记在属性上 可以标记在set方法上 还可以标记在构造器上
required 表示是否必须注入 如果是true 没找到注入的值会报错 如果是false就不会报错 可以自己去设置required的值
默认通过byType 没有就通过byName
@Qualifier("userServiceImpl") 一般配合@Autowired 一起使用
@A
@Resource(name = "userDaoImpl") 可以指定名称去进行注入
@Value("aaa") 将aaa直接赋值给了变量a
private String a ;
@Value("${ccc}") 将properties文件中的ccckey的值赋值给了变量b
private String b;
代码使用案例解释:
package com.xiaowang.controller; import com.xiaowang.entity.Pet; import com.xiaowang.service.PetService; import com.xiaowang.service.impl.PetServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; @Controller public class PetController { //以前使用方式:创建PetController的对象去调用service对象,service对象再去调用dao对象去完成,但是这样的话就很不方便,每一次要用对象的时候都要去new一个来用 // private PetService petService = new PetServiceImpl(); //spring方法:将对象都真装在容器里面,这样的话每一次要用直接调用set方法就行。这样的话不管什么时候想用都可以 // @Resource(name = "petServiceImpl")//这个注解会在spring的容器中去找到name对应的id值,给注入到下面去,这个注解必须指定id @Autowired //自动装配,也就是通过byType去spring里面找到对应的资源注入进去,如果type没有就通过byName去找 // @Qualifier("petServiceImpl")// 这个就是通过名称去找,一般和@Autowired配合使用 private PetService petService; @Value("aaa")//这样子使用Value注解的话,就是直接将aaa赋值给了a,这还不如直接声明的时候=aaa,所以这个Value注解一般不这样用 private String a; @Value("${url}")//注入配置文件中的数据进来,一般value注入用于将配置文件的值注入进来,注意这里要在spring的配置文件中,将要注入的properties文件加载到spring中 private String b; //初始化方法的注解 @PostConstruct //故名思意 构造器之前 public void init(){ System.out.println("petController的init"); } //销毁方法的注解 @PreDestroy //顾名思义 销毁之前 public void destroy(){ System.out.println("petController的destroy"); } /* public void setPetService(PetService petService) { this.petService = petService; }*/ public Pet add(String username, String password){ System.out.println("controller执行"); System.out.println(a); System.out.println(b); return petService.add(username,password); } }
类注解
@Controller 一般写在类上 标记当前是控制器 将当前类的对象放入容器中 id默认是类名首字母小写
@Service 一般写在类上 标记当前是业务层 将当前类的对象放入容器中 id默认是类名首字母小写
@Repository 一般写在类上 标记当前是持久层 将当前类的对象放入容器中 id默认是类名首字母小写
@Component 一般写在类上 不标记是哪一层 将当前类的对象放入容器中 id默认是类名首字母小写
值注入注解
@Autowired 自动装配,默认从spring中获取bean并且自动注入到标记的内容上,可以是类、属性、方法、字段等;这个注解有一个required属性值为true或false,默认是true,代表必须要找到注入的值,不然要报错
@Qualifier( ) 具体注入的位置,一般配合着@Autowired使用,
@Value(“aaa”) 直接赋值
@Value(“${url}”) 通过取配置文件中的properties的具体name取注入所标记的值
作用范围注解
@Scope(“prototype”) 一般加在类上 指定作用范围
@PostConstruct 初始化方法注解
@PreDestroy 销毁方法注解
代理模式:代理模式是Java中一种常用的设计模式,主要就是提供一个代理对象对外提供一个方法,然后用过代理对象去访问目标表对象;
通俗理解:
实际就是创建一个与目标对象(实际要操作的对象)相同类型的代理对象proxy,使用这个proxy对象去访问目标对象完成操作,在这个proxy对象中还可以有其他的功能去丰富目标对象的功能。服务器最后调用的就是这个proxy对象,这样就完成了代理增强。
代理模式最主要的就是在不改变原来代码(就是目标对象)的情况下实现功能的增强
静态代理:相当于是自己写了一个代理类,在调用的时候调用的是代理类,代理类中的处理还是原生的处理逻辑,不过在前后添加上需要增强功能的代码。
缺点:需要为每一个被代理的对象都创建一个代理类。
public interface PetService {
Pet add(String username, String password);
}
public class PetServiceImpl implements PetService {
public Pet add(String username, String password) {
System.out.println("目标对象:petService执行");
return petDao.add(username,password);
}
}
//代理对象 public class ProxyPetService implements PetService { // 代理对象自己是不做事的,是交给目标对象做,相当于就是创建个目标对象,然后通过目标对象去完成方法,所以需要引入目标对象 private PetService petService; //通过构造器去完成目标对象的创建 public ProxyPetService(PetService petService) { this.petService = petService; } @Override public Pet add(String username, String password) { //可以加功能啥的了,就是要实现的方法,为了方便,就写一句输出就行 System.out.println("代理增强功能1...."); Pet add = petService.add(username, password); //增加代理功能 System.out.println("代理增强功能2...."); return add; } }
@Test
public void testJingTai(){
//要实现静态代理,就要将代理对象和目标对象进行关联起来,相当于就是要将目标对象放到代理对象中去
PetService petService = new PetServiceImpl();//创建目标对象
PetService proxyPetService = new ProxyPetService(petService);//创建代理对象
//外部调用目标对象去完成增强功能呢
Pet xiaowang = proxyPetService.add("xiaowang", "123456");
}
注意:目标对象和代理对象要实现用一个接口,目标对象完成自己的事情,代理对象调用目标对象,不仅可以完成目标对象的事情,自己还可以再加一些功能上去
动态代理模式最大的优势就是不用自己去写一个代理对象,它的代理对象会在java文件编译的时候,通过Java反射(javac的过程)去创建的。所以减少了程序员的工作。动态代理的实现有两种方式,现在来介绍一下
注意:JDK动态代理的目标对象必须有接口实现
jdk实现动态代理理解:
主要是通过Java反射包里面的类和接口去完成的。反射包里面有三个类:InvocationHandler、Method、Prixy
public interface PetDao {
Pet add(String username, String password);
}
public class PetDaoImpl implements PetDao {
@Override
public Pet add(String username, String password) {
System.out.println("petDao执行");
return new Pet(username,password);
}
}
public class PetDaoInvoinvokecation implements InvocationHandler { //在执行器中还是需要有目标对象呀 private Object obj; public PetDaoInvoinvokecation(Object obj) { this.obj = obj; } //invoke参数解释:proxy:代理对象,Method:代理对象的方法,args:代理对象所需的参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理模式不需要自己去写代理对象,而是在程序运行编译的时候就会创建的 //真正的操作是由目标对象来完成的,所以通过method方法去完成执行器,里面参数就是目标对象以及目标对象要操作的方法的参数 System.out.println("jdk增强之前.."); Object invoke = method.invoke(obj, args); System.out.println("jdk增强之后.."); return invoke; } }
注意:是反射包( java.lang.reflect )里面的InvovationHandler接口哈
@Test public void testJDK(){ PetDao petDao = new PetDaoImpl();//目标对象 // 代理对象执行器 PetDaoInvoinvokecation petDaoInvoinvokecation = new PetDaoInvoinvokecation(petDao); /* 代理对象,通过反射完成Proxy.newProxyInstance() newProxyInstance参数: 类加载器:(代理对象的类型,也就是目标对象的)、 目标对象所实现的接口:(返回是一个数组,因为一个类可以实现多个接口嘛)、 执行器: */ //代理对象的类型,是目标对象所实现的接口类型之一,就相当于静态代理的时候一样,目标对象和代理对象都是实现的同一个接口 PetDao o = (PetDao) Proxy.newProxyInstance(petDao.getClass().getClassLoader(), petDao.getClass().getInterfaces(), petDaoInvoinvokecation); Pet xiaowang = o.add("xiaowang", "123456");//代理对象执行方法 System.out.println(xiaowang); }
jdk动态模式原理:
主要是由反射包( java.lang.reflect )里面的InvocationHandler、Method、Prixy三个类来完成的。
①通过实现InvocationHandler接口去创建代理对象的执行器(重写invoke方法)
invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { }
invoke方法里面就是通过method去执行具体的代理对象的方法
②然后通过Prixy的newProxyInstance方法去获取代理对象,
newProxyInstance参数:类加载器:(代理对象的类型,也就是目标对象的)、 目标对象所实现的接口:(返回是一个数组,因为一个类可以实现多个接口嘛)、 执行器:
③通过②里面的代理对象去调用目标对象的方法
注意代码中的注释!!
cglib代理模式是生成一个子类去完成的,并且不需要去实现接口
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
@Repository //将此类定义成持久层,然后将其放入spring中,里面可以加value参数的,加了就是这个持久层的id,默认是类名首字母是小写
//@Component //这个注解就是直接将此类放入spring中,不会标记此类的类型,一般用于普通类要放入spring的
public class PetDaoImplCGLIB{
public Pet add(String username, String password) {
System.out.println("目标对象 petDao执行");
return new Pet(username,password);
}
}
//拦截器去完成,记得是cglib包下面的哈 public class PetDaoImplCGLIBIntercepter implements MethodInterceptor { /* intercept参数介绍: Object:目标对象 Method:目标对象的方法 Object:目标对象方法的参数 MethodProxy:代理方法 */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("cglib代理增强1....."); //调用目标对象执行方法 Object o1 = methodProxy.invokeSuper(o, objects); System.out.println("cglib代理增强2....."); return o1; } }
4. 测试
cglib的原理就是在运行的时候,cglib会自动去生成一个子类,在子类里面去完成增强操作(就是拦截器里面),这里我们来验证cglib就用保存它的子类来查验,也就是将它自动生成的类放在一个指定路径下去看。
编写cglib的测试类
@Test public void testCGLIB(){ //设置生成动态代理类的路径 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\xiaowang\\kuangjia\\spring\\cglib存放位置"); //创建Enhancer对象,就相当于jdk中的proxy类一样的,作用就是去执行方法的啦~ Enhancer enhancer = new Enhancer(); //设置代理类的目标对象 enhancer.setSuperclass(PetDaoImplCGLIB.class); //创建拦截器 PetDaoImplCGLIBIntercepter petDaoImplCGLIBIntercepter = new PetDaoImplCGLIBIntercepter(); //设置回调函数 设置增强对象 enhancer.setCallback(petDaoImplCGLIBIntercepter); //创建代理对象,类型是目标对象类型哈 PetDaoImplCGLIB o = (PetDaoImplCGLIB) enhancer.create(); //执行方法 Pet zhangsan = o.add("zhangsan", "123456"); System.out.println(zhangsan); }
执行结果:
也执行出来了呀
注意:我们可以将生成的类反编译一下,就可以看到生成的类继承了我们的目标类,所以可以说明,cglib的原理其实就是cglib在底层继承了目标类然后去实现增强的
cglib动态代理原理:
①创建拦截器:继承MethodInterceptor的 intercepter的类,在拦截器中重写intercerpt( )方法,就是增强+目标对象的方法调用,返回拦截器
②在测试这边创建一个类似proxy的子类对象enhancer,然后设置这个代理对象的类型(setSuperclass(目标对象的类型.class完成))
③创建一个拦截器,enhancer通过去设置回调函数(setCallback(拦截器))
④创建代理对象enhancer.create(),代理对象的类型要和目标对象一致哈,然后通过代理对象去完成方法的调用
静态代理就是自己需要手动去写一个代理对象,实现目标对象所实现的接口,而动态代理却简化了这一步,不需要自己去写代理对象,而是在java编译的时候,通过反射完成了对象的创建。
OOP (Object Oriented Programming) 面向对象编程
AOP (Aspect Oritented Programming) 面向切面编程)
两者不是替代的关系而是互补的,AOP主要就是在不改变原本代码功能的情况下去新增功能
在spring中AOP很重要,可以理解为,AB业务互不影响且要去实现C的业务,但是我们需要在执行A业务的时候,在C业务功能的基础上,让A业务完成想要的功能,让B业务完成B业务需要的更强大的功能,前提就是C业务是基础业务是不能被更改的,不然就要影响其他业务。
所以在这个时候,AOP就做出了一个动作,A业务在C业务的想要添加的方法前面砍断,添加一个新的A自己的业务上去后,再将重新组合好的业务给织入原来的C业务方法那里去,继续执行C业务后面的功能。B业务一样的。
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.18</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.6</version> <scope>runtime</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.18</version> </dependency>
<?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:aop="http://www.springframework.org/schema/aop" 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.xs"> <!-- 注解实现:--> <!-- 开启注解:注解是需要开启的--> <!-- <context:annotation-config></context:annotation-config>--> <!-- 扫描注解:扫描注解的话,是辉自动开启注解的,所以上面开启注解语句就可以不要了--> <context:component-scan base-package="com.xiaowang"> </context:component-scan> <!-- 加载配置文件到spring里面,这样就可以去获取/置文件中的数据了--> <!-- classpath:就是加载resource类路径下面的配置文件--> <context:property-placeholder location="classpath:db.properties"/> <!-- 开启动态代理 proxy-target-class: 如果为true那就是类代理,也就是jdk实现动态代理 如果是false那就是cglib实现动态代理 默认是false --> <aop:aspectj-autoproxy proxy-target-class="false"/> </beans>
最主要的就是加上了:
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xs
注意:使用AOP的话是需要开启动态代理的哈,proxy-target-class的值true代表jdk实现,false代表cglib实现
开始创建切入类
//放入spring中 @Component @Aspect //这个标签是标记这个类是一个切面 public class PetAspect { /** * 标记再哪里什么时候执行切入 * @param joinPoint */ // execution:里面写的就是切入点,切入点有很多种语法,下面这个是最精准的切入点,直接定位到了具体的方法,切入点的语法有很多,下面一一介绍 @Before("execution(public com.xiaowang.entity.User com.xiaowang.PetService.impl.PetServiceImpl.login(java.lang.String,java.lang.String))") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void before(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("before之前执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); } }
执行结果:
我们可以看到,就在login方法的前面就添加进去了我们的对象的方法,我们拦截的东西也可以看到了,这里同时还有多种类似于@before的注解,其功能就是在这个方法切入点的哪一个位置进行切入,例如还有:
/** * 标记再哪里什么时候执行切入, * @befor 代表在标记位置的前面切入 * @param joinPoint */ // execution:里面写的就是切入点,切入点有很多种语法,下面这个是最精准的切入点,直接定位到了具体的方法,切入点的语法有很多,下面一一介绍 @Before("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void before(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("before之前执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); } /** * @After 在切入点的后面切入方法功能进去 * 相当于finally执行,就是必备执行 * @param joinPoint */ @After("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void after(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("after执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); } /** * @AfterReturning 相当于正常执行,就是拦截方法正确执行后就可以执行 * @param joinPoint */ @AfterReturning("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void afterReturning(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("afterReturning执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); } /** * 拦截的语句那里抛出异常后被执行 * @param joinPoint */ @AfterThrowing("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void afterThrowing(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("afterThrowing执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); }
①拦截方法正常执行的时候结果:
②拦截方法有异常的时候结果:
如果使用AOP去管理事务的话,那我们就可以在before里面写事务的开启,在afterReturning中提交事务,在afterThrowing中去回滚事务,
所以在@afterThrowing这里的话就可以再加一点属性,去设置哪些地方需要回滚,哪些地方不需要回滚
/** * 拦截的语句那里抛出异常后被执行,相当于catch * @param joinPoint */ @AfterThrowing(value = "execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))",throwing = "e") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void afterThrowing(JoinPoint joinPoint,Exception e){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("afterThrowing执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); //事务 System.out.println("抛出异常进行回滚"+e.getMessage()); }
独特的环绕执行:
/**
* 环绕执行,就像过滤器一样,再dofilt前面执行,后面执行
* @param proceedingJoinPoint
* @return
*/
@Around(value = "execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")
//想将下面这个方法切入到测试类中testJDK()方法的前面执行
public Object around(ProceedingJoinPoint proceedingJoinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行
//切入面中,是可以获取很多被拦截的东西,
System.out.println("around之前执行");
Object proceed = proceedingJoinPoint.proceed();//代理的方法执行
System.out.println("around之后执行");
return proceed;
}
使用xml实现当然前期的依赖呀、配置文件的头部那些基本的都是要有哈,和注解实现的前面一样,区别就在于对切面类的编写的时候,切入点不用写在注解上了,而是写在xml文件里了,也就是删掉or注释掉切面类中的注解呀
//放入spring中 @Component @Aspect //这个标签是标记这个类是一个切面 public class PetAspect { /** * 标记再哪里什么时候执行切入, * @befor 代表在标记位置的前面切入 * @param joinPoint */ // execution:里面写的就是切入点,切入点有很多种语法,下面这个是最精准的切入点,直接定位到了具体的方法,切入点的语法有很多,下面一一介绍 // @Before("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void before(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("before之前执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); } /** * @After 在切入点的后面切入方法功能进去 * 相当于finally执行,就是必备执行 * @param joinPoint */ // @After("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void after(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("after执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); } /** * @AfterReturning 相当于正常执行,就是拦截方法正确执行后就可以执行 * @param joinPoint */ // @AfterReturning("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void afterReturning(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("afterReturning执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); } /** * 拦截的语句那里抛出异常后被执行,相当于catch * @param joinPoint */ // @AfterThrowing(value = "execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))",throwing = "e") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public void afterThrowing(JoinPoint joinPoint,Exception e){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("afterThrowing执行"); System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs())); System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName()); System.out.println("拦截的位置为:"+joinPoint.getStaticPart()); System.out.println("代理对象为:"+joinPoint.getThis()); System.out.println("目标对象为:"+joinPoint.getTarget()); //事务 System.out.println("抛出异常进行回滚"+e.getMessage()); } /* *//** * 环绕执行,就像过滤器一样,再dofilt前面执行,后面执行 * @param proceedingJoinPoint * @return *//* //@Around(value = "execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))") //想将下面这个方法切入到测试类中testJDK()方法的前面执行 public Object around(ProceedingJoinPoint proceedingJoinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行 //切入面中,是可以获取很多被拦截的东西, System.out.println("around之前执行"); Object proceed = proceedingJoinPoint.proceed();//代理的方法执行 System.out.println("around之后执行"); return proceed; } */ }
注意:我们在切面类上面加了@Aspect 这个注解后,就代表着这是一个切面了,就不用在xml中去通过<aop:aspect ref=“userAspect”>去定义这个类未切面类,两种方法二选一即可。
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" xmlns:aop="http://www.springframework.org/schema/aop" 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"> <!-- 注解实现:--> <!-- 开启注解:注解是需要开启的--> <!-- <context:annotation-config></context:annotation-config>--> <!-- 扫描注解:扫描注解的话,是辉自动开启注解的,所以上面开启注解语句就可以不要了--> <context:component-scan base-package="com.xiaowang"> </context:component-scan> <!-- 加载配置文件到spring里面,这样就可以去获取/置文件中的数据了--> <!-- classpath:就是加载resource类路径下面的配置文件--> <context:property-placeholder location="classpath:db.properties"/> <!-- 装配注解,也就是将各个注解进行关联起来--> <!-- 开启动态代理 proxy-target-class: 如果为true那就是类代理,也就是jdk实现动态代理 如果是false那就是cglib实现动态代理 默认是false --> <aop:aspectj-autoproxy proxy-target-class="false"/> <!-- 使用xml去实现aop--> <aop:config> <!-- 定义切面信息,如果切面类上使用了@Aspect注解,就可以不用定义这个切面类信息,但是切面点啥的还是要配置--> <aop:aspect ref="petAspect"> <!-- 定义切面点--> <aop:pointcut id="add" expression="execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))"/> <!-- 实现切面类的增强功能--> <aop:before method="before" pointcut-ref="add"/> <aop:after-returning method="afterReturning" pointcut-ref="add"/> <aop:after-throwing method="afterThrowing" throwing="e" pointcut-ref="add"/> <aop:after method="after" pointcut-ref="add"/> </aop:aspect> </aop:config> </beans>
直接精准到一个方法上面去 execution( public com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String)) 任意权限修饰符 execution( com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String)) 无返回类型 execution( void com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String)) 有返回类型 execution( !void com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String)) 任意返回类型 execution( * com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String)) 任意参数 execution( * com.zlt.service.impl.UserServiceImpl.login(..)) 类中的任意方法 execution( * com.zlt.service.impl.UserServiceImpl.*(..)) 类中以指定内容开头的方法 execution( * com.zlt.service.impl.UserServiceImpl.select*(..)) 包中的任意类的任意方法不包含子包下面的类 execution( * com.zlt.service.impl.*.*(..)) 包中及其下的任意类的任意方法 execution( * com.zlt.service..*.*(..))
通过事务来完成spring于muybatis的整合,流程:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- spring依赖--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.18</version> </dependency> <!-- AOP的依赖--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.18</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.6</version> <scope>runtime</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.18</version> </dependency> <!-- spring事务相关的依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.24</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>6.0.6</version> </dependency> <!-- mybatis相关依赖--> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!-- !!!!spring和mybatis整合,需要用一个插件依赖!!!!--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.0</version> </dependency> <!-- 使用自己的连接池德鲁伊--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <!-- 使用逆向工程完成数据库操作--> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.4.0</version> </dependency> <!-- 分页--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.2.0</version> </dependency> <!-- 日志--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.1</version> </dependency> <dependency> <!-- 添加一个lombok依赖,这样一会实体类的构造器、get、set方法就不用手写了--> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> </dependencies>
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--注解扫描 默认开启注解支持--> <context:component-scan base-package="com.xiaowang"> </context:component-scan> <!--开启动态代理--> <aop:aspectj-autoproxy/> <!--引入外部文件,这里是为了将db.properties里面的数据源给引入,然后后面放入mybatis的配置中--> <context:property-placeholder location="classpath:db.properties"/> <!-- 创建数据源--> <!-- 因为我们要使用的是外部的德鲁伊连接池,所以需要用xml去配置数据源,这样就和数据库连接上了--> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 将db.propertise中的连接池数据给注入到我们的数据源中--> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- 这里还可以根据数据库连接池去配置最大连接数等等--> </bean> <!--配置mybatis东西--> <!-- 首先配置会话工厂,这个工厂是mybatis-spring这个插件里面的--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据源--> <property name="dataSource" ref="druidDataSource"/> <!-- 在mybatis中还会有很多映射文件,比如mapper包里面的mapper.xml那些对某一个类进行xml操作的配置文件--> <property name="mapperLocations" value="classpath:mapper/**.xml"/> <!-- 别名配置呀,我使用spring的就可以注释了--> <!--<property name="typeAliasesPackage" value="com.xiaowang.entity"/>--> <!-- 引入mybatis自己的配置文件,我使用spring的就可以注释了--> <!-- <property name="configLocation" value="classpath:mapper_config.xml"/>--> <!-- 添加插件,这里演示分页插件--> <property name="plugins"> <!-- 直接使用spring的分页插件,需要注入数据,分页的对象是数组,所以要用数组的方式去注入数据--> <array> <bean class="com.github.pagehelper.PageInterceptor"> <!-- 分页里面还可以定义自己的东西--> <property name="properties" > <props> <!-- 方言--> <prop key="helperDialect">mysql</prop> <!-- 分页参数合理化--> <prop key="reasonable">true</prop> </props> </property> </bean> </array> </property> <!-- 一些配置,比如开启缓存啥的--> </bean> <!-- 接口扫描, 也就是mybatis中我们去getMapper的时候一样的意思,spring可以去扫描这些接口,然后我们就可以拿到对应的数据--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 扫描的接口,也就是需要扫描哪些包里面的接口,一般放包名是最好的--> <property name="basePackage" value="com.xiaowang.mapper"/> <!-- 设置会话工厂,也就是相当于去会话工厂的数据里面去扫描接口,sqlSessionFactory--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> <!-- 事务管理--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据源,也就是要对哪些进行事务管理--> <property name="dataSource" ref="druidDataSource"/> </bean> <!-- 事务是注解管理的--> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- 注意事务管理对象的id和注解管理的manager要一致才行哈--> </beans>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!--数据库驱动--> <classPathEntry location="E:\MySQL\mysql-connector-java-5.1.16\mysql-connector-java-5.1.16\mysql-connector-java-5.1.16-bin.jar"/> <context id="DB2Tables" targetRuntime="MyBatis3"> <commentGenerator> <property name="suppressDate" value="true"/> <property name="suppressAllComments" value="true"/> </commentGenerator> <!--数据库链接地址账号密码--> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql:///weibo67?useUnicode=true&useSSL=false&characterEncoding=utf-8&rewriteBatchedStatements=true" userId="root" password="youngc777"> </jdbcConnection> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!--生成Model类存放位置--> <javaModelGenerator targetPackage="com.xiaowang.entity" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <!--生成映射文件存放位置--> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <!--生成Dao类存放位置--> <javaClientGenerator type="XMLMAPPER" targetPackage="com.xiaowang.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <!--生成对应表及类名--> <table tableName="weibo" domainObjectName="Weibo" enableCountByExample="true" enableUpdateByExample="true" enableDeleteByExample="true" enableSelectByExample="true" selectByExampleQueryId="true"></table> <table tableName="user" domainObjectName="User" enableCountByExample="true" enableUpdateByExample="true" enableDeleteByExample="true" enableSelectByExample="true" selectByExampleQueryId="true"></table> </context> </generatorConfiguration>
mybatis逆向工程就完成了呀,可以看见那些功能都创建了,然后就可以去创建controller、servcie进行操作了呀
所有的配置都是一条线的,所以弄清楚这条线,就知道该怎么一步步去配置了,可以记一下:线路:
①添加对应的依赖
②在spring中配置相应的bean对象。并且开启注解扫描、开启动态代理、引入外部文件等
③bean对象:
1. 创建数据源: 数据源里面存的就是对数据库连接的参数信息,相当于与数据库进行连接
2. 配置mybatis:先创建会话工厂、然后注入数据源、还可以配置一些别名呀、添加插件呀什么的,那些你需要mybatis操作的东西
3. 接口扫描:配置要扫描的接口,这些接口一般就是mapper里面的接口了,还有就是会话工厂里面要实现的接口了,具体要实现什么接口就配置对应的接口就好了呀
4. 事务管理:通过spring的jdbc去进行事务管理,在配置事务管理的时候,需要将数据源给注入到事务管理中去噢,注意这里的 id 必须要和实现事务管理的transaction-manager一致
5.注解实现事务:通过注解去完成事务的管理,也就是在对应的service呀等要对事务进行管理的时候,加上注解@Transaction,就可以实现事务管理了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。