目的:解决企业应用开发的复杂性。
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
范围:任何Java应用
前身: interface21框架。
Spring致力于J2EE应用的各层的解决方案,而不是仅仅专注于某一层的方案。可以说Spring是企业应用开发的“一站式”选择,并贯穿表现层、业务层及持久层。然而,Spring并不想取代那些已有的框架,而是与它们无缝地整合。
1、Spring core:核心容器
spring框架的基本功能,以bean的方式组织和管理Java应用中的各个组件及其关系,使用BeanFactory来产生和管理Bean,它是工厂模式的实现。主要实现控制反转IoC和依赖注入DI、Bean配置以及加载。2、Spring AOP:Spring面向切面编程
通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。AOP把一个业务流程分成几部分,例如权限检查、业务处理、日志记录,每个部分单独处理,然后把它们组装成完整的业务流程。每个部分被称为切面或关注点。3、Spring context:Spring上下文
Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。提供框架式Bean访问方式,其他程序可以通过Context访问Spring的Bean资源。4、Spring DAO
DAO模块主要目的是将持久层相关问题与一般的的业务规则和工作流隔离开来。Spring 中的DAO提供一致的方式访问数据库,不管采用何种持久化技术,Spring都提供一致的编程模型。Spring还对不同的持久层技术提供一致的DAO方式的异常层次结构。Spring的DAO模块对JDBC进行了再封装,隐藏了Connection、 Statement、ResultSet等JDBC API,使DAO模块直接继承JdbcDaoSupport类。5、Spring ORM(Object Relation Mapper)对象关系映射模块
方便ORM框架的集成,包括hibernate、JDO实现、TopLink和IBatis SQL Map等。Spring为所有的这些框架提供了模板之类的辅助类,达成了一致的编程风格。 Spring的ORM模块对ORM框架如Hibernate等进行了封装,Spring能够管理、维护Hibernate,使用时可直接继承HibernateDaoSupport类,该类内置一个HibernateTemplate。Hibernate的配置也转移到Spring配置文件中。6、Spring Web模块
Web模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。Web层使用Web层框架,可选的,可以是Spring自己的MVC框架,或者提供的Web框架,如Struts、Webwork、tapestry和jsf。Web模块用于整合Web框架,将Web框架也纳入Spring的管理之中。如Spring提供继承方式与代理方式整合Struts,继承方式不需要更改任何配置文件,只把Action继承自ActionSupport即可,但会对Spring产生依赖。代理方式需要在struts-config.xml中配置,由Spring全盘代理,因此可以使用Spring的各种资源、拦截器等。7、Spring MVC
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。Spring的MVC框架提供清晰的角色划分:控制器、验证器、命令对象、表单对象和模型对象、分发器、处理器映射和视图解析器。Spring支持多种视图技术。什么是ioc
ioc英文全称为Inversion of Control,中文翻译为控制反转,意思为把控制权转让给对方,它是一种设计思想,在java中意思是把创建对象的过程(控制权)交 给第三方管理者,程序需要用到对象的时候直接去三方管理者取,从而实现对象之间的解耦。而spring容器就是这个第三方管理者,负责对象(bean)的创建和管 理。
传统的对象间调用方式是利用组合耦合在一块,一旦某个被调用类的类名发生了改变,所有调用者耦合的地方都得修改,费时费力,利用spring容器管理只需要 修改相关配置即可,,从而降低对象之间的解耦性。
例如(未使用容器之前):
public interface UserDao { void add(); } public class UserDaoImpl implements UserDao { public void add() { System.out.println("hello spring"); } } public interface UserService { void add(); } public class UserServiceImpl implements UserService { UserDao userDao = new UserDaoImpl(); public void add() { userDao.add(); } } public class Test { public static void main(String[] args) { //调用业务层 UserService userService = new UserServiceImpl(); userService.add(); } }这里UserServiceImpl和UserDaoImpl耦合在了一块,假如UserDaoImpl名字发生了改变,那么所有耦合了UserDaoImpl的地方都得改名,耦合的地方少到可以手动修改,一旦存在多处耦合,那将会是很痛苦的一件事。
现在使用容器解决这个问题:
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userDao" class="com.test.dao.UserDaoImpl"> </bean> </beans> public class UserServiceImpl implements UserService { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserDao userDao = (UserDao)context.getBean("userDao"); public void add() { userDao.add(); } }总结
使用spring管理后,只需要通过xml进行bean的注册(也可使用注解),spring的bean容器会进行对象的创建和管理。
当某个类要使用对象时,直接去容器中取,而不是自己创建。
当被使用的对象类修改名或者要使用对应接口新实现的类对象时,只需要修改相关注册的地方类名,程序不用改动,从而实现解耦。
程序中需要面向接口编程(利用多态),否则没有意义。
如果把ioc看作程序的一种解耦思想,那么di就是其思想的实现,依赖指的是bean对象的注入依赖于容器,注入指的是注入bean对象的属性。主要有以下几种注 入方式。
下标注入
<!--有参构造下标注入--> <bean id="user" class="com.create.way.User"> <constructor-arg index="0" value="测试有参构造下标注入-"/> </bean> 类型注入
<!--有参构造类型注入--> <bean id="user" class="com.create.way.User"> <constructor-arg type="java.lang.String" value="测试有参构造类型注入"/> </bean> 名称注入
<bean id="user" class="com.create.way.User"> <constructor-arg name="name" value="测试有参构造名称注入"/> </bean> P命名空间注入
public class People { String name; int age; public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "People{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
c命名空间注入
可以将多个配置文件导入到一个里面,使用合并的那个就可以,便于团队合作。
例如存在三份配置文件beans、beans2、beans3,合并成一个applicationContext为:
<import resource="beans.xml"></import> <import resource="beans2.xml"></import> <import resource="beans3.xml"></import>单例模式(默认): 始终只有一个对象
public class TestPanel { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans2"); People people = context.getBean("people",People.class); People people2 = context.getBean("people",People.class); System.out.println(people==people2); } }原型模式:每次从容器中取得时候都会创建一个新的对象。
public class TestPanel { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans2"); People people = context.getBean("people",People.class); People people2 = context.getBean("people",People.class); System.out.println(people.hashCode()); System.out.println(people2.hashCode()); System.out.println(people==people2); } }request、sessio、applicati在web开发中使用。
spring装配bean的三种方式
1.xml配置(手动)
2.java代码
3.注解(自动)
通过set后面的名去容器中自动查找对应的beanid相同的bean注入,beanid唯一
public class Cat { public void shut(){ System.out.println("猫叫了"); } } public class Dog { public void run(){ System.out.println("狗跑了"); } } public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); People people = context.getBean("people",People.class); people.getCat().shut(); people.getDog().run(); } } 在容器中查找和自己属性对应类型相同的bean注入,只能有一个同类型的bean
jdk1.5,spring2.5开始支持
@Autowired可以用在属性上,也可以用在set方法上。
@Autowired的required默认为true,表示该属性不能为null,如果设置为false,则可以为null。
@Autowired可以和 @Qualifier搭配使用,指定beanid注入
public class People { //如果required设置为false,说明该属性可以为null @Autowired(required = false) Cat cat; @Autowired @Qualifier(value="dog1") Dog dog; public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } } @Resource和@Autowired作用一样,都可以对bean进行自动装配。
@Autowired默认通过byType注入,如果存在多个bean,则通过byName注入
@Resource默认通过byName注入,如果找不到名,则通过byType注入
在企业开发中,通常按照mvc模式分层,除了自动装配注解外,还有其他很多常用的注解。
@Component用于注册bean,相当于
<bean id="people" class="com.test.autoried.People"> </bean> @Scope(“prototype”)定义作用域,相当于
@Value(“测试”)用于给属性设置,可用于属性和set方法上,相当于
@Service、@Controller、@Repository常用于业务层、控制层、持久层,作用和@Component一样。
在开发中,如果要使用这些注解,必须现在对应的包下开启扫描。
<context:component-scan base-package="com.test.autoried"/> <context:annotation-config/> @Component @Scope("prototype") public class People { @Value("测试") String name; @Autowired Cat cat; @Autowired Dog dog; public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } public String getName() { return name; } public void setName(String name) { this.name = name; } }xml配置和注解的比较
xml配置代码量较大,但便于统一管理。注解代码少,但管理比较分散。
在实际开发中,二者可结合使用。
java配置相当于用一个java类取代xml文件的作用,是spring boot中常用的方式。JavaConfig是spring的一个子项目,是spring4后的核心功能。
//@Configuration表示一个配置类,其本身也是@Component,会被spring容器管理,相当于beans.xml //@Import可以把多个配置类合成一个 @Configuration @ComponentScan("pojo") @Import(BeanConfig2.class) public class BeanConfig { //注册一个bean,相当于bean标签,方法名相当于bean id //返回值相当于标签的class属性 //return返回的就是注入的对象 @Bean public User getUser(){ return new User(); } } @Component public class User { String name; public String getName() { return name; } @Value("测试config") public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } } public class TestConfig { public static void main(String[] args) { //使用的时候需要用AnnotationConfig上下文获取 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); User user = (User)context.getBean("user"); System.out.println(user.getName()); } } 代理类是程序员写好的,编译期间代理类就确定
角色分析
抽象角色:一般使用抽象类或者接口。
真实角色: 被代理的角色。
代理角色:代理真实角色的角色,一般会额外做些事情(存在的意义)。
客户:访问代理代理角色的对象。
。 //接口 public interface House { void sendKey(); } //真实角色 public class HouseOwer implements House{ @Override public void sendKey() { System.out.println("房东给钥匙!"); } } //代理角色 public class Proxcy implements House { private HouseOwer houseOwer; public Proxcy(HouseOwer houseOwer) { this.houseOwer = houseOwer; } @Override public void sendKey() { conduct(); houseOwer.sendKey(); seeHouse(); } public void conduct(){ System.out.println("中介帮房东打广告!"); } public void seeHouse(){ System.out.println("中介带客户看房!"); } } //客户 public class TestProcy { public static void main(String[] args) { HouseOwer houseOwer = new HouseOwer(); Proxcy proxcy = new Proxcy(houseOwer); proxcy.sendKey(); } }代理模式的好处
使真实角色更好的专注于自己的业务处理,其他公共处理交给代理角色。
业务处理和公共处理由不同角色完成,实现了任务的分工,方便于公共业务的管理和扩展。
缺点
一个真实角色对应一个代理角色,当有很多真实角色时,代码量会很大。
动态代理的代理类是在运行期间由处理程序动态生成的,角色划分和静态代理一样,常见的有jdk动态代理(只能代理接口)和Cglib动态代理,这里研究的时jdk 动态代理。
//生成代理类的类(处理程序) public class ProxcyInvocationHandler implements InvocationHandler { //被代理的接口 private Object target; public void setTarget(Object target) { this.target = target; } //生成的代理类 public Object getInstance(){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } //处理代理实例 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { conduct(); Object result = method.invoke(target,args); seeHouse(); return result; } public void conduct(){ System.out.println("中介帮房东打广告!"); } public void seeHouse(){ System.out.println("中介带客户看房!"); } } public class TestProcy { public static void main(String[] args) { //真实角色 HouseOwer houseOwer = new HouseOwer(); //生成代理类的对象 ProxcyInvocationHandler proxcyInvocationHandler = new ProxcyInvocationHandler(); //设置要代理的对象 proxcyInvocationHandler.setTarget(houseOwer); //动态生成代理类 House proxcy = (House)proxcyInvocationHandler.getInstance(); proxcy.sendKey(); } }总结
动态代理和静态代理具有同样的好处,但克服了静态代理中真实类必须手动编写代理类的缺点,而是由处理程序自动帮我们生成代理类,只要是真实类实现了相同接口,那么就可以调用处理程序为这些真实类生成对应的代理类,大大的节省了代码量,同时可以在处理程序的invoke方法调用前后加上想要的功能,spring的aop机制就是这个原理。
在oop编程思想中,我们会根据事物的属性和特征进行分类,所以当我们想在一个方法调用前面或者后面加一些其他功能,例如日志、权限、事务等功能,我们一般会想到的是直接在对应方法前后进行功能添加,做的更高级一点,就是利用oop的思想将其封装成一个工具类或者一个父类,然后由业务类去继承父类或者直接调用工具类对应方法。但是这些都避免不了一个问题,我们的代码中一定会去显示的调用这些方法,而且会存在多处调用,一旦不需要某个功能了,我们必须手动去删除这些方法,不但费时费力,还增大了代码量,并且业务逻辑和这些功能耦合到了一块,功能的修改可能会影响到业务逻辑,所以aop思想出现了,其主要目的就是为了解决这一问题。
在aop编程思想中,为了解决这个问题,也会把这些功能抽取出来形成一个类(切面),但这个类不会直接和业务类交互,而是通过一种机制(动态代理)在程序运行期间动态的把这个类里面的方法(通知)插入到业务类(目标)方法(切入点)的前后(连接点),于是乎我们的业务代码在不做任何改动的情况下就添加了这些功能,从而实现了解耦,减少了代码的冗余,可以更加专注自己业务层的实现,这就是aop编程思想,也称面向切面编程。
Spring中的AOP可以认为是aop思想的一种实现,当然还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。aop中一些专业术语如下:
(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
Spring AOP 中有 5 中通知类型,分别如下:
测试
首先导入aop所需要的包
<dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> </dependencies> public interface UserService { void add(); void delete(); void update(); void select(); } public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } public void delete() { System.out.println("删除了一个用户"); } public void update() { System.out.println("修改了一个用户"); } public void select() { System.out.println("查看了一个用户"); } } public class BeforeLog implements MethodBeforeAdvice { //method:要执行的目标对象的方法 //args:参数 //target:目标对象 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了"); } } public class AfterLog implements AfterReturningAdvice { public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了"+method.getName()+"方法,返回结果为:"+o); } } <?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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--注册bean--> <bean id="userService" class="com.aop.service.UserServiceImpl"/> <bean id="afterLog" class="com.aop.log.AfterLog"/> <bean id="beforeLog" class="com.aop.log.BeforeLog"/> <!--方式一--> <!--配置aop,需要导入aop约束--> <aop:config> <!--切入点,execution表示要执行的位置--> <aop:pointcut id="pointcut" expression="execution(* com.aop.service.UserServiceImpl.*(..) )"/> <!--执行环绕增强--> <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans> public class TestAop { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService)context.getBean("userService"); userService.add(); } }文章部分参考:https://www.cnblogs.com/jxxblogs/p/12143015.html