spring基础
spring IOC (控制反转)
搭建spring环境
下载spring框架:http://maven.springframework.org/release/org/springframework/spring/
我们在解压后的压缩包里面找到5个jar包:
spring-aop.jar 开发AOP特性时需要的JAR
spring-beans.jar 处理Bean的jar
spring-context.jar 处理spring上下文的jar
spring-core.jar spring核心jar
spring-expression.jar spring表达式
三方提供的日志jar: commons-logging.jar 日志
-
编写配置文件
建议下载sts工具:https://spring.io/tools/sts/
或者使用开发工具IDEA
-
开发spring程序(IOC)
新建一个Java项目,在src目录下新建一个applicationContext.xml文件
在上述文件下配置自己的bean
<bean id="student" class="完整的包名"> </bean>
spring帮助我们new对象
ApplicationContext conext = new ClassPathXmlApplicationContext("applicationContext.xml") ; //执行从springIOC容器中获取一个 id为student的对象 Student student = (Student)conext.getBean("student") ;
<bean id="student" class="org.syy.entity.Student"> <property name="stuNo" value="2"></property> <property name="stuName" value="ls"></property> <property name="stuAge" value="20"></property> </bean>
使用student需要有student实体类,用property赋值需要在三个属性中需要有set方法
*/
Student student = (Student)conext.getBean(“student”) ;
System.out.println(student);
-
总结
IOC(控制反转)也可以称之为DI(依赖注入):
控制反转:将 创建对象、属性值 的方式 进行了翻转,从new、setXxx() 翻转为了 从springIOC容器 getBean()
依赖注入:将属性值 注入给了属性,将属性 注入给了bean,将bean注入给了ioc容器;总结:ioc/di ,无论要什么对象,都可以直接去springioc容器中获取,而不需要自己操作(new\setXxx())
因此之后的ioc分为2步:1. 先给springioc中存放对象并赋值 2.拿
在ioc中定义bean的前提:该bean的类 必须提供了 无参构造
IOC容器赋值
- 基本类型(8个基本类型+String) 直接通过value 赋值
- 对象类型,ref=“需要引用的id值”,因此实现了 对象与对象之间的依赖关系
-
依赖注入方式:
-
1.set注入:通过setXxx()赋值
赋值,默认使用的是 set方法();
依赖注入底层是通过反射实现的。
<property…> -
2.构造器注入:通过构造方法赋值
<constructor-arg value="ls" type="String" index="0" name="name"> </constructor-arg>
需要注意:如果 的顺序 与构造方法参数的顺序不一致,则需要通过type或者index或name指定。
-
3.p命名空间注入
引入p命名空间
xmlns:p=“http://www.springframework.org/schema/p”<bean id="course" class="org.lanqiao.entity.Course" p:courseHour="300" p:courseName="hadoop" p:teacher-ref="teacher">
给对象类型赋值null : <property name="name" > <null/> -->注意 没有<value> </property> 赋空值 "" <property name="name" > <value></value> </property>
-
使用注解定义bean(推荐)
通过注解的形式 将bean以及相应的属性值 放入ioc容器。
需要配置扫描器:
<!-- 配置扫描器 -->
<context:component-scan base-package="org.syy.dao.impl,org.syy.service.impl,org.syy.aop">
</context:component-scan>
Spring在启动的时候,会根据base-package在 该包中扫描所有类,查找这些类是否有注解 @Component(“studentDao”),如果有,则将该类 加入spring Ioc容器。
@Component细化:(可分为三层)
dao层注解:@Repository
service层注解:@Service
控制器层注解:@Controller
spring AOP(面向方面编程)
把一个普通的类变成有特定功能的类
类 -> “通知” :
1.实现接口
-
前置通知
-
引入jar包 aopliance.jar + aspectjweaver.jar
-
编写前置通知类:logBefore 需要继承前置通知的一个接口
package org.syy.aop; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class LogBefore implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("前置通知:目标对象:"+target+",调用的方法名:"+method.getName()+",方法的参数个数:"+args.length); } }
然后在applicationContext.xml配置
<!-- “前置通知”类 --> <!-- =========连接线的一方========= --> <!--将通知纳入springIOC容器--> <bean id="logBefore" class="org.syy.aop.LogBefore"></bean> <!-- 将addStudent() 和 通知 进行关联 --> <aop:config> <!-- 配置切入点 (在哪里执行通知 ) --> <!-- =========连接线的另一方========= --> <aop:pointcut expression="execution(public void org.syy.service.impl.StudentServiceImpl.deleteStudentByNo(int)) or execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))" id="pointcut"/> <!-- advisor:相当于 链接切入点 和切面的线 --> <aop:advisor advice-ref="logBefore" pointcut-ref="pointcut"/> </aop:config>
然后我们可以写测试类
public static void testAop() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml") ; IStudentService studentService = (IStudentService)context.getBean("studentService") ; Student student = new Student(); studentService.addStudent(student); }
-
-
后置通知
package org.syy.aop; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class LogAfter implements AfterReturningAdvice{ public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("后置通知:目标对象:"+target+",调用的方法名:"+method.getName()+",方法的参数个数:"+args.length+",方法的返回值:"+returnValue); } }
在applicationContext.xml中配置
<!-- 后置通知 --> <!-- 将通知纳入springIOC容器 --> <bean id="logAfter" class="org.syy.aop.LogAfter"></bean> <aop:config> <!-- 切入点(连接线的一端) 业务类的方法 --> <aop:pointcut expression="execution(public void org.syy.service.impl.StudentServiceImpl.deleteStudentByNo(int)) or execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))" id="pointcut2"/> <!-- 连接线的另一端:通知类 --> <aop:advisor advice-ref="logAfter" pointcut-ref="pointcut2"/> </aop:config>
-
异常通知
根据异常通知接口的定义可以发现,异常通知的实现类 必须编写以下方法:
public void afterThrowing([Method, args, target], ThrowableSubclass): a.public void afterThrowing(Method, args, target, ThrowableSubclass) b.public void afterThrowing( ThrowableSubclass)
package org.syy.aop; import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; public class LogException implements ThrowsAdvice{ //必须重写该方法 public void afterThrowing(Method method,Object[] args,Object target, Throwable ex) { System.out.println("异常通知:目标对象:"+target+",方法名:"+method.getName()+",方法的参数个数:"+args.length+",异常类型:"+ex.getMessage()); } }
在applicationContext.xml 中配置
<bean id="logException" class="org.syy.aop.LogException"></bean> <aop:config> <!-- 切入点(连接线的一端) 业务类的方法 --> <aop:pointcut expression="execution(public void org.syy.service.impl.StudentServiceImpl.deleteStudentByNo(int)) or execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))" id="pointcut3"/> <!-- 连接线的另一端:通知类 --> <aop:advisor advice-ref="logException" pointcut-ref="pointcut3"/> </aop:config>
-
环绕通知
在目标方法的前后、异常发生时、最终等各个地方都可以 进行的通知,最强大的一个通知;
可以获取目标方法的 全部控制权(目标方法是否执行、执行之前、执行之后、参数、返回值等)在使用环绕通知时,目标方法的一切信息 都可以通过invocation参数获取到环绕通知 底层是通过拦截器实现的。
package org.syy.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class LogAround implements MethodInterceptor{ public Object invoke(MethodInvocation invocation) throws Throwable { Object result = null; try { System.out.println("用环绕通知实现的[前置通知]..."); /*该语句之前为前置通知*/ result = invocation.proceed();//控制目标方法执行 addStudent() System.out.println("用环绕通知实现的[后置通知]...:"); // System.out.println("-----------------目标对象target:"+invocation.getThis()+",调用的方法名:"+invocation.getMethod().getName()+",方法的参数个数:"+invocation.getArguments().length+",返回值:"+result); /*该语句之后为后置通知*/ } catch (Exception e) { //异常通知 System.out.println("用环绕通知实现的[异常通知]..."); } return result;//目标方法的返回值 } }
在applicationContext.xml 中配置
<!-- 环绕通知 --> <bean id="logAround" class="org.syy.aop.LogAround" ></bean> <aop:config> <aop:pointcut expression="execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))" id="pointcut4"/> <aop:advisor advice-ref="logAround" pointcut-ref="pointcut4" /> </aop:config>
2.注解实现通知
- 首先开启对aop注解的支持
<!-- 开启注解对AOP的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Aspect //声明该类 是一个 通知
public class LogBeforeAnnotation {
}
通过注解形式 实现的aop,如果想获取 目标对象的一些参数,则需要使用一个对象:JointPoint
注解形式的返回值:
a.声明返回值 的参数名:
@AfterReturning( pointcut= "execution(public * addStudent(..))" ,returning="returningValue" )
public void myAfter(JoinPoint jp,Object returningValue) {//returningValue是返回值,但需要告诉spring
System.out.println("返回值:"+returningValue );
//注解形式实现aop时,通知的方法的参数不能多、少
package org.syy.aop;
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.springframework.stereotype.Component;
@Component("LogAnnotation")
@Aspect//此类是一个通知
public class LogAspectAnnotation {
//前置通知
@Before(value = "execution(public void org.syy.service.impl.StudentServiceImpl.deleteStudentByNo(int)) or execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))")//定义切点
public void myBefor(JoinPoint jp) {
System.out.println("《注解形式-前置通知》:目标对象:"+jp.getTarget()+",方法名:"+jp.getSignature().getName() +",参数列表:"+ jp.getArgs().length);
}
/*后置通知*/
@AfterReturning(pointcut = "execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))",returning = "returningValue")
public void myAfter(JoinPoint jp,Object returningValue) {
System.out.println("《注解形式-后置通知》:目标对象:"+jp.getTarget()+",方法名:"+jp.getSignature().getName() +",参数列表:"+ jp.getArgs().length+",返回值:"+returningValue);
}
//环绕通知
@Around("execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))")
public void myAround(ProceedingJoinPoint jp){
//方法执行之前:前置通知
System.out.println("《【环绕】方法之前:前置通知");
try {
//方法执行时
jp.proceed() ;//执行方法
//方法执行之后:后置通知
System.out.println("《【环绕】方法之前之后:后置通知");
} catch (Throwable e) {
//异常通知
System.out.println("《【环绕】发生异常时:异常通知");
} finally {
//最终通知
System.out.println("《【环绕】最终通知");
}
}
//异常通知
@AfterThrowing(pointcut = "execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))",throwing = "e")
public void myException(JoinPoint pj, NullPointerException e)
{
System.out.println("《注解形式-异常通知》----e:"+e.getMessage());
}
//最终通知
@After("execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))")
public void myFinally()
{
System.out.println("注解形式-最终通知-----");
}
}
3.基于Schema配置
- 接口方式通知:public class LogAfter implements AfterReturningAdvice
Schema方式通知:
a.编写一个普通类 public class LogAfter {}
b.将该类 通过配置,转为一个“通知” - 如果要获取目标对象信息:
注解、schema:JoinPoint
接口:Method method, Object[] args, Object target
编写配置类
package org.syy.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class LogSchema {
//后置通知方法 :JoinPoint适用于注解
public void afterReturning(JoinPoint jp,Object returnValue) throws Throwable {
System.out.println("schema-后置通知:目标对象:"+jp.getThis()+",调用的方法名:"+jp.getSignature().getName()+",方法的参数个数:"+jp.getArgs().length+",方法的返回值:"+returnValue);
}
public void before() {
System.out.println("schema-前置通知...");
}
public void whenException(JoinPoint jp,NullPointerException e) {
System.out.println("schema-异常:" +e.getMessage());
}
//注意:环绕通知 会返回目标方法的返回值,因此返回值为Object
public Object around(ProceedingJoinPoint jp) {
System.out.println("schema-环绕通知:前置通知");
Object result = null ;
try {
result = jp.proceed() ;//执行方法
System.out.println("schema-"+jp.getSignature().getName()+","+result);
System.out.println("schema-环绕通知:后置通知");
}catch(Throwable e) {
System.out.println("schema-环绕通知:异常通知");
}
return result ;
}
}
在applicationContext.xml 中配置
<bean id="logSchema" class="org.syy.aop.LogSchema"></bean>
<aop:config>
<aop:pointcut expression="execution(public * org.syy.service.impl.StudentServiceImpl.addStudent(..))" id="pcShema"/>
<aop:aspect ref="logSchema">
<aop:before method="before" pointcut-ref="pcShema"/>
<aop:after-returning method="afterReturning" returning="returnValue" pointcut-ref="pcShema"/>
<aop:after-throwing method="whenException" pointcut-ref="pcShema" throwing="e"/>
<!-- 环绕 -->
<aop:around method="around" pointcut-ref="pcShema"/>
</aop:aspect>
</aop:config>