Spring
1.一个jar对应一个关键字
类似于spring-beans.jar 对应
spring-context.jar对应
2.为了编写时有提示,自动生成提示信息下载,使用tool suit
开发spring(IOC程序)
平时JAVA创建实例的过程.class
main(){
New一个新的对象....
Set属性...
打印属性.....
}
Spring创建实例过程.xml--------相当于new了
<bean id="....." class="...." property = "....">
<property name= "...." value="....."> </property>
.....
</bean>
///spring上下文对象
-----test-------
ApplicationContext context = new ClassPathxmlApplicationContext(xml的名字);加载xml中配置
类名 实例名 = (强转类名)context.getBean(“id名称”);
打印....
总结:
对象创建放入了xml中,xml中产生的所有对象,被spring放入了一个称之为spring IOC容器
SpringIOC发展史:
原来—>new的方式创建对象非常零散,造成后期维护较为麻烦
IOC容器
超级工厂:控制反转
反转:获取对象的方式getBean(“id”);
以前是自己产生,现在是直接去IOC中拿
为了更加清晰的理解IOC,在大会上IOC更名为DI(依赖注入)从new(),setxxx(),变为了从bean中get
把属性值注给了属性,把属性注入给了bean,把bean 注入给了IOC容器。
之后IOC操作分两步:
- 先从ioc中存放对象并且赋值
- 从ioc中拿
eclipse中类的标志有S代表此类已经在IOC中放入了实例
IOC容器赋值:
-
简单类型: 直接value
-
复杂类型 :若在类中包含一个另外的类作为此类的属性,中不能使用value,必须使用 ref=“引用的id”,实现了对象与对象的依赖关系
依赖注入的四种方式:
1.property注入
实质上对于bean中属性的赋值仍然是调用类属性中的set 方法…
使用到了反射:根据字符串获取字符串所代表的一切
例子:
<bean id = "person02" class = "com.bean.person"><!-- 使用property 调用的为set get 方法 -->
<property name="age" value = "19"></property>
<property name="lastname" value = "张三"></property>
</bean>
2.构造器注入(构造方法赋值 constructor-arg):
- 无构造方法时默认调用无参构造
- 当已经存在构造方法时,需要给对应参数的构造方法
public person(String lastname, Integer age, String gender) {
this.lastname = lastname;
this.age = age;
this.gender = gender;
System.out.println("有参构造器");
}
<bean id = "person03" class = "com.bean.person"><!-- 调用有参构造器进行赋值 -->
<constructor-arg name="age" value = "19"></constructor-arg>
<constructor-arg name = "lastname" value = "lifei"></constructor-arg>
<constructor-arg name = "gender" value = "male"></constructor-arg>
</bean>
若不想写name就使用index作为索引位置,或者按照构造方法中的顺序省略不写
3.通过p命令空间注入
<bean id = "person03" class = "com.bean.person" p:age="19" p:lastname="zhangsan" p:gender="male">
简单类型:
p:属性名=“属性值”
引用类型:
p:属性名-ref=“引用的id”
注意:
-
多个p赋值之间要留空格。
-
无论是String还是Int/short/long/,在赋值时都是value = “值”,因此建议将此种情况需要配合name\type进行区分。
示例:注入各种集合类型
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class AllCollectionType {
private List<String> list;
private String[]array;
private Set<String> set;
private Map<String,String>map;
private Properties props;
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public String[] getArray() {
return array;
}
public void setArray(String[] array) {
this.array = array;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Properties getProps() {
return props;
}
public void setProps(Properties props) {
this.props = props;
}
@Override
public String toString() {
String strContent = "";
for (String str:array) {
strContent += str+",";
}
return "list:"+this.list+"\nset"+this.set+"\nmap"+this.map+"\nprops"+this.props+"\narray"+strContent;
}
}
<?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 id="collection" class = "AllCollectionType">
<property name = "list">
<list>
<value>足球</value>
<value>篮球</value>
<value>排球</value>
</list>
</property>
<property name = "array">
<array>
<value>数组1</value>
<value>数组2</value>
<value>数组3</value>
<value>数组4</value>
</array>
</property>
<property name = "set">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
<value>set4</value>
</set>
</property>
<property name = "map">
<map>
<entry>
<key>
<value>foot</value>
</key>
<value>足球</value>
</entry>
<entry>
<key>
<value>head</value>
</key>
<value>篮球</value>
</entry>
</map>
</property>
<property name = "props">
<props>
<prop key="dididi"></prop>
</props>
</property>
</bean>
</beans>
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext abc = new ClassPathXmlApplicationContext("applicationcontext.xml");
AllCollectionType a = (AllCollectionType)abc.getBean("collection");
System.out.print(a);
}
}
end:
list:[足球, 篮球, 排球]
set[set1, set2, set3, set4]
map{foot=足球, head=篮球}
props{dididi=}
array数组1,数组2,数组3,数组4
注意:
- array,set都可以使用list标签,list也可以变其他,但是不建议。
- value属性和标签的区别
-
若使用value标签,标签可以加上type标签,但是type中的类名要写全类名。
-
若要赋值为null不用写value,直接写。
-
若要赋值为空值,直接写“”。
自动装配(只适合于ref类型):
约定优于配置
- byName
<bean id="collection" class = "AllCollectionType" autowire="byName">
AllCollectionType中有一个ref属性person(属性名),并且该ioc容器中恰好有一个bean的id也是person
autowire=“byName”
byName其实就是byId
自动寻找:bean的id值=该类的属性名
- byType
<bean id="collection" class = "AllCollectionType" autowire="byType">
在bean 中找到其他bean的类型和AllCollectionType中相同的类型ref
只能有一个Bean满足条件
若有多个相同的类型,则报错
- constructor
<bean id="collection" class = "AllCollectionType" autowire="byType">
在这个类中寻找在bean 中找到其他bean的类型和AllCollectionType类中的构造方法参数的类型一致
- 可以在头文件中将所有bean设置为自动装配
default-autowire = “类型” 默认对所有里面的类进行装配
但是子标签可以覆盖
自动装配可以减少代码量,但是会降低程序的可读性,不建议大量使用
使用注解定义bean: 使用注解的形式将bean以及相应的属性值,放入ioc容器
@Component(bean中想要的id)
Dao, service层都能放入,可读性不好
细化:
- 首先在xml中使用扫描器声明要扫描的包(这步我经常忘记了。。。。)
<context:component-scan base-package="要导入的包"></context:component-scan>
- 在包下,若有准备的放入的bean,使用注解,如果有,则将该类加入ioc容器
@Repository(bean中想要的id)
代表修饰这个类是Dao层
@Service(bean中想要的id)
代表修饰Service层
@Controller(bean中想要的id)
修饰控制层
@Autowire
在service层下的Dao进行配置,相当于对bean下的某个dao属性进行bytype类型自动装配
若想根据byName进行自动装配在@autowire下再加**@Qualiier(要的ID)**进行装配
Aop面向切面编程(aspectj)
切入点
切面:切入add()后面
AOP:每当执行add()之前自动执行的方法
一个普通的类 -> 特定功能的类
-
继承一个类
-
实现一个接口
-
注解
-
配置
类 -> 通知:
- 实现接口
前置通知实现步骤:
- 导入aopaliance.jar,aspectjweaver.jar
- 配置
- 编写
add():业务方法
log():自动执行的通知,即aop的前置通知
public class LogBefore implements MethodBeforeAdvice{
//前置
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.print("前置通知....");
}
}
<aop:config >
<aop:pointcut expression="execution(public void org.lanqiao.service.Studentserviceimp.addStudent())" id="pointcut"/><!--程序在哪个点进行切入 ,在哪里执行 -->
<!--advisor 相当于链接切入点和切面的线 -->
<!--advice-ref线的右边-->
<!--pointcut-ref线的左边-->
<aop:advisor advice-ref="logbefore" pointcut-ref="pointcut"/><!--程序在那个点进行切入 -->
</aop:config>
继承MethodBeforeAdvice接口
后置通知:继承AfterReturningAdvice接口
<aop:config >
<aop:pointcut expression="execution(public void org.lanqiao.service.Studentserviceimp.addStudent())" id="pointcut1"/><!--程序在哪个点进行切入 ,在哪里执行 -->
<!--advisor 相当于链接切入点和切面的线 -->
<!--advice-ref线的右边-->
<!--pointcut-ref线的左边-->
<aop:advisor advice-ref="logafter" pointcut-ref="pointcut1"/><!--程序在那个点进行切入 -->
</aop:config>
异常通知(接口AfterAdvice):
异常通知在类中编写以下方法:
public void afterThrowing(Method method,Object args,Object target,Throwable ex) {
}
OR
public void afterThrowing(Throwable ex) {
}
直接在bean中配置
<aspect><aop:exctept>
环绕通知:在目标方法的前后,最终等各个地方都可以进行的通知,是最强大的通知:可以获取目标方法的全部控制权(目标方法是否执行、执行之前、执行之后、参数、返回值 )
public class LogAround implements MethodInterceptor{
Object result= null;//控制目标方法的执行,执行的其实是addStudent()
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
//在这方法之前执行的,为前置通知
System.out.print("target:"+invocation.getThis()+"调用的方法名"+invocation.getMethod().getName()+"方法的参数个数:"+invocation.getArguments().length);
System.out.print("用环绕通知实现的前置通知");
result = invocation.proceed();
System.out.print("用环绕通知实现的后置通知");
//之后的代码:后置通知
}catch (Exception e) {
// TODO: handle exception
System.out.print("用环绕通知实现的异常通知");
}
return result;
}
}
在XML中配置
<bean id = "LogException" class = "org.lanqiao.aop.LogAround"></bean>
<aop:config>
<aop:pointcut expression="execution(public void org.lanqiao.service.Studentserviceimp.addStudent())" id="pointcut4"/><!--程序在哪个点进行切入 ,在哪里执行 -->
<!--advisor 相当于链接切入点和切面的线 -->
<!--advice-ref线的右边-->
<!--pointcut-ref线的左边-->
<aop:advisor advice-ref="LogException" pointcut-ref="pointcut4"/><!--程序在那个点进行切入 -->
</aop:config>
在使用环绕通知时候,目标方法的一切信息,都可以通过invocation获取
使用注解实现通知AOP
XML中开启对注解支持<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
**@Component(ID)**放入IOC容器
@Aspect
声明该类是一个通知
- @Before(定义切点):
给任何一个方法前加此注解,代表前置通知
- @AfterReturning(同上)
后置通知
-
通过注解形式实现的AOP,如果想要获取目标对象的一些参数,则需要使用一个对象:JointPoint
@Aspect public class LogAfter { @AfterReturning(pointcut="execution(public * org.lanqiao.service.Studentserviceimp.addStudent())",returning = "returningValue") public void afterReturning(JoinPoint jp,Object returningValue){//固定返回值书写方法 若没有返回值,就不要写 System.out.print("后置通知"+jp.getTarget()+"方法名"+jp.getSignature().getName()+"参数"+Arrays.toString(jp.getArgs())+"返回值"+returningValue); } }
注解形式的返回值:
a.声明返回值的参数名: returning = “returningValue”
-
@Around()
环绕通知
@Around("execution(public * org.lanqiao.service.Studentserviceimp.addStudent())")
public Object myAround(ProceedingJoinPoint jp) {
System.out.print("前置通知");
try{
//方法执行时候
jp.proceed();//执行方法
}catch (Throwable e) {
// TODO: handle exception
//异常通知
}
finally {
//最终通知
}
return result;
}
结构一定是某个方法(前置通知)+try(执行时候通知)catch(异常通知)+finally(最终通知)
会返回方法的返回值
- @AfterThrowing()
异常通知
@AfterThrowing(pointcut = "execution(public * org.lanqiao.service.Studentserviceimp.addStudent())",throwing="e")//注意进行申明
public void myException(NullPointerException e) {//只捕获特定的异常种类
System.out.print("异常通知"+e.getMessage());
}
- @After()
最终通知
@After("execution(public * org.lanqiao.service.Studentserviceimp.addStudent())")
public void myAfter() {
System.out.print("uqwdhuqdhu");
}
通过配置让一个类变成一个通知
基于Schema配置
类似于实现接口的方式
Schema方式通知:
a.编写一个普通类,public class LogAfter()
b.将该类通过配置,转化为一个“通知”
将准备转为通知的类纳入IOC容器
<aop:aspect ref="通知的类">
<aop:before method = "before" point-ref="连接的方法"/><!-- 前置通知 -->
<!--后置通知 -->
<aop:after-returning method = "afterReturning" point-ref="连接的方法" />
<aop:after-throwing method = "方法" point-ref="连接的方法异常时候" />
</aop:aspect>
若想获得方法参数,在类中定义好参数,类似接口方式
Spring开发Web项目
-
Web项目如何初始化IOC容器:
思路:当服务启动(tomcat),通过监听器将SpringIOC容器初始化一份
使用SpringWeb包,web项目的jar包是存入WEB-INF/lib
web项目启动时候,会自动加载web.xml,要在web.xml中加载监听器(IOC容器初始化)
<context-param>
<!-- 容器的父类ContextLoader中有一个属性contextConfigLocation,该属性值保存着容器配置文件applicationContext.xml的位置 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<!-- 配置spring-web.jar的监听器,此监听器可以在服务器启动时初始化IOC容器
初始化IOC容器,要告诉监听器此容器的位置:context-param
-->
<listener-class>org.springframework.web.context.ContextLoaderListener.class</listener-class>
</listener>
如果不写applicationContext.xml的位置,则放入WEB-INF下为默认位置,文件名不能改
WEB项目的拆分:
根据什么拆分?
-
三层结构
UI
Service applicationcontextSerivce.xml
Dao
公共数据库
-
功能结构
例如:学生相关配置 applicationcontextStudent.xml
如何合并这么多.xml
<context-param>
<!-- 容器的父类ContextLoader中有一个属性contextConfigLocation,该属性值保存着容器配置文件applicationContext.xml的位置 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml
</param-value>
</context-param>
-
在classpath中添加IOC容器
-
classpath:applicationContext-*.xml使用通配符进行配置,简化操作
-
在主文件的applicationContext中引入其他IOC容器(不建议)
<import resource=""/>
当所有东西都传入IOC容器以后,WEB项目在查询时候仍然获取不到目标的Service,原因如下
当发送request请求时候,目标访问的是Servlet容器,导致找不到Service
所以,在servlet中一定要设置一个init()的方法
init(){ //ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); //web项目获取Spring上下文对象context ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); //在Servlet容器中,通过getBean获取Ioc容器中的bean StudentService = StudentService(context).getBean("studentService"); }