一、Spring之旅

    xiaoxiao2022-07-06  252

    第一部分 Spring之旅

    创建 Spring 的主要目的

    用来替代更加重量级的企业级 Java 技术 相对 EJB(Enterprise JavaBean, 企业级 JavaBean),增强了 POJO(Plain Old Java object)功能,使其具备了之前只有 EJB 和 其他企业级 Java 规范才有的功能。 现在 EJB 也采用了 依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)。

    Spring 采用的4种关键策略(降低 Java 开发的复杂性)
    基于 POJO 的轻量级和最想侵入性编程通过依赖注入和面向接口实现松耦合。基于切面和惯例进行声明式编程通过切面和模板建设样本式代码
    依赖注入

    DI 让互相协作的软件组件保持松散耦合 没有耦合的代码什么做不了 三种注入方式:

    field注入构造器注入setter注入 @Controller public class PageController { @Autowired private PageService pageService; //简单的使用例子 public List<Page> listFoo() { return pageService.list(); } }
    应用切面

    AOP 允许你将遍布应用各处的功能分类出来形成可重用的组件

    数据集成与访问

    Spring 的 JDBC 和 DAO(Data Access Object)模块抽象了样板式代码,使数据库代码变得简单明了,还可以避免因数据库资源失败而引发的问题。 Spring 的 ORM(Object-Relational Mapping)模块建立在对 DAO 的支持上,为多个 ORM 框架提供了以重构建 DAO 的简便方式,对许多流行的 ORM 框架进行了集成,包括 Hibernate、Java Persisternce API、Java Data Object 和 iBATIS SQL Maps,其事务管理支持所有 ORM 和 JDBC。

    Web

    提供 MVC(Model View Controller)模式 提供暴露和使用 REST API 的良好支持

    装配 Bean

    @Component 注释

    表明该类会作为组件类,告知 Spring 要为这个类创建 bean 不过组件扫描默认使不启用的,需要显示配置 Spring 命令其寻找带有 @Component 注解的类,并为其创建 bean。 通过 @CompoentScan 扫描

    //将扫描的 bean 标识为 use。 @Component("use") public class Page implements ComDisc{ ... } //扫描多个包 包名 @Configuration @ComponentScan(basePackages={"record", "video"}) public class Page{ ... } //扫描多个包中的类或接口 @Configuration @ComponentScan(basePackagesClasses={record.class, video.class}) public class Page{ ... }

    @Autowired 注解 用在构造器和属性的 Setter 方法上

    @Autowired public void setRecord(Record record) { this.record = record; }

    若没有匹配 bean,Spring 可能会抛出异常,可将 @Autowired 的 required 属性设置为 false。

    @Autowired(required = false) public void setRecord(Record record) { this.record = record; }

    @Inject 是 java 依赖注入规范,@Autowired 是 Spring 特有的。

    @Configuration 注解 表明该类是个配置类

    @Bean 注解 告诉 Spring 这个方法要返回一个对象,该对象要注册为 Spring 应用上下文中的 bean,且可以指定一个名字。

    @Bean(name="lonelyHeartsCludBand") public Page record() { return new Record(); }

    高级配置

    环境和 profile 使用嵌入式数据库

    @Bean(destroyMethod = "record") public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .addScript("classpath: schema.sql") .addScript("classpath: test-data.sql") .build(); }

    EmbeddedDatabaseBuilder 可搭建一个嵌入式的 Hypersonic 数据库,其模式(schema)定义在 schema.sql 中, 测试数据通过 test-data.sql 加载。 EmbeddedDatabaseBuilder 创建的 DataSource 适合开发环境,但是不适合生产环境 JNDI 管理的 DataSource 更加适合生产环境,对于简单的集成和开发测试环境来说会带来不必要的复杂性。 QA 环境中可以选择不同的 DataSource 配置,配置 Commons DBCP 连接池。

    @Profile 注解指定某个 bean 属于哪个 profile 只有在 development profile 激活时才会创建

    @Configuration public class DataSourceConfiguration { @Profile("development") @Bean public DataSource embeddedDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath*:schema.sql") .addScript("classpath*:test-data.sql") .build(); } @Profile("qa") @Bean public BasicDataSource basicDataSource() { BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("org.h2.Driver"); ds.setUrl("jdbc:h2:tcp://localhost/~/spitter"); ds.setUsername("sa"); ds.setPassword(""); ds.setInitialSize(5); //初始大小 ds.setMaxTotal(10); //数据库连接池大小 return ds; } @Profile("production") @Bean public DataSource dataSource() { JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("/jdbc/SpittrDS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource)jndiObjectFactoryBean.getObject(); } }

    @Conditional 注解 用于带有 @Bean 的注解的方法上,形成条件化的 bean,如果给定的条件计算结果为 true,就创建,反之忽略 bean。

    @Profile 基于 @Conditional 和 Condition 实现,其本身也使用了 @Conditional 注解

    @Primary 注解 告诉 Spring 最喜欢的方案,声明首选 bean。

    @Qualifier 注解 使用限定符的主要方式,可以和@Autowired 和 @Inject 协同使用,在注入的时候指定想要注入的是哪个 bean。

    @Autowired @Qualifier("iceCream") public void setDessert(Dessert dessert) { this.dessert = dessert; }

    注入外部的值 @PropertySource 注解

    //声明属性源 @PropertySource("classpath:/com/soundsystem/app.properties") public class test(){ ... } //这个属性文件或加载到 Spring 的 Environment 中,稍后可以从这里检索属性。

    面向切面的 Spring

    定义切面 @Aspect 注解 定义切面类

    @Aspect public class Audience { //这是表演之前 @Before("execution(* com.spring.learn.index.Performance.perform(..))") public void silenceCellPhones(){ System.out.println("观众的手机静音"); } //表演之前 @Before("execution(* com.spring.learn.index.Performance.perform(..))") public void takeSeats() { System.out.println("观众落座"); } //表演之后 @AfterReturning("execution(* com.spring.learn.index.Performance.perform(..))") public void applause() { System.out.println("掌声雷动"); } //表演失败 @AfterThrowing("execution(* com.spring.learn.index.Performance.perform(..))") public void demandRefund() { System.out.println("要求退款"); } } 前置通知(@Before):通知方法在目标方法调用之前执行。后置通知(@After):通知方法在目标方法返回或抛出异常后调用。返回通知(@AfterReturning):通知方法在目标方法返回后调用。异常通知(@AfterThrowing):通知方法在目标方法抛出异常之后调用。环绕通知(@Around):通知方法在目标方法封装起来。

    @Pointcut @Pointcut 所注释的值是一个切点表达式

    @Aspect public class Audience { @Pointcut("execution(* com.spring.learn.index.Performance.perform(..))") public void performance(){} //这是表演之前 @Before() public void silenceCellPhones("performance()"){ System.out.println("观众的手机静音"); } //表演之前 @Before("performance()") public void takeSeats() { System.out.println("观众落座"); } //表演之后 @AfterReturning("performance()") public void applause() { System.out.println("掌声雷动"); } //表演失败 @AfterThrowing("performance()") public void demandRefund() { System.out.println("要求退款"); } }

    参考文献: Spring 实战(第4版) [美] Craig Walls 著 张卫滨 译 人民邮电出版社

    最新回复(0)