生活随笔
收集整理的这篇文章主要介绍了
SpringFramework-IOC(依赖注入)+AOP(面向切面编程)
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
文章目录
- 简介
- IOC(控制反转)
- HelloSpring
- IOC创建对象方式
- Spring配置文件
- 依赖注入(DI)
- bean的作用域
- bean 的自动装配(autowire)
- 使用注解开发
- bean(@Component)
- 属性注入(@Value(""))
- 衍生的注解
- 使用注解自动装配(@Autowired)
- 作用域
- 使用Java的方式配置Spring
- 代理模式
- AOP
- 使用Spring的API接口实现
- 自定义切入点类
- 注解实现AOP(@Aspect)
- 整合myBatis
- 事务
简介
中文文档
Spring Framework中文文档
-
目的:解决企业应用开发的复杂性
-
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
-
范围:任何Java应用
-
Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
-
Rod Johnson是spring Framework的创始人
-
Spring的理念:使现在的技术更加的容易使用,整合现有的技术框架
-
SSH:Struct2 + Spring + Hibernate
-
SSM:SpringMvc + Spring +MyBatis
-
优点:
- Spring是一个开源的免费的框架
- Spring是一个轻量级的、非入侵式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务的处理
- 对框架的整合支持
Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
Github地址
maven仓库:
<dependency><groupId>org.springframework
</groupId><artifactId>spring-webmvc
</artifactId><version>5.2.8.RELEASE
</version>
</dependency>
<dependency><groupId>org.springframework
</groupId><artifactId>spring-jdbc
</artifactId><version>5.2.8.RELEASE
</version>
</dependency>
七大模块
- Spring Boot:
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个微服务
- 约定大于配置
- Spring Cloud:
IOC(控制反转)
- 之前,程序主动创建对象,控制权在底层
- 使用set注入,程序不再有主动权,变成了被动的接收对象,降低系统耦合性
- 控制反转是一种通过描述(XML或者注解)并通过第三方去生产或获取特定对象的方式,在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection ,DI)
控制反转IOC(Inversion of Control),是一种设计思想,没有IoC的程序中,我们使用面向对象的编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方
HelloSpring
创建一个测试类
public class Hello {private String str
;public String
getStr() {return str
;}public void setStr(String str
) {this.str
= str
;}@Overridepublic String
toString() {return "Hello{" +"str='" + str
+ '\'' +'}';}
}
在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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="hello" class="com.huang.pojo.Hello"><property name="str" value="Spring"/></bean>
</beans>
测试调用
public class myTest {@Testpublic void testHello(){ApplicationContext context
= new ClassPathXmlApplicationContext("beans.xml");Hello hello
= (Hello
) context
.getBean("hello");System
.out
.println(hello
.toString());}
}
IOC创建对象方式
- 在配置文件加载的时候,容器中管理的对象就已经被创建了
- 无参构造:property
- 有参构造:constructor-arg
<bean id="hello" class="com.huang.pojo.Hello">
<property name="str" value="Spring"/>
<constructor-arg index="0" value="huang"/><constructor-arg type="java.lang.String" value="yaohui"/><constructor-arg name="str" value="hanhan"
</bean>
Spring配置文件
alias(别名)
<alias name="hello" alias="hello2"/>
Bean的配置
- id:bean的唯一标识符,也相当于我们学的对象名
- class:bean对象所对应的全限定名
- name:也是别名,可以起多个别名,可以用(,;空格)等来间隔
<bean id="myname" class="com.huang.pojo.Hello" name="name1 name2,name3"></bean>
import
<import resource="beans.xml"/>
依赖注入(DI)
set注入
- 依赖注入:set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中所有的属性,由容器来注入
- 包含类型:
bean | ref | idref | list | set | map | props | value | null
<bean id="student" class="com.huang.pojo.Student"><property name="name" value="persistenthuang"/><property name="hello" ref="hello"/><property name="books"><array><value type="java.lang.String">红楼梦
</value><value type="java.lang.String">西游记
</value><value type="java.lang.String">水浒传
</value><value type="java.lang.String">三国演义
</value></array></property><property name="hobby"><list><value type="java.lang.String">听歌
</value><value type="java.lang.String">看电影
</value></list></property><property name="card"><map><entry key="cad1" value="123"/><entry key="cad2" value="123456"/></map></property><property name="games"><set><value type="java.lang.String">LoL
</value><value type="java.lang.String">红警
</value><value type="java.lang.String">刀塔
</value></set></property><property name="wife"><null/></property><property name="properties"><props><prop key="学号">22111222
</prop><prop key="班级">三年二班
</prop><prop key="姓名">persistenthuang
</prop></props></property></bean>
c命名和p命名空间注入
P命名空间:可以直接注入属性的值(property)
xmlns:p="http://www.springframework.org/schema/p"
//通过set注入
<bean id="hello3" class="com.huang.pojo.Hello" p:str="huang"></bean>
C命名空间:
xmlns:c="http://www.springframework.org/schema/c"
//通过构造器注入
<bean id="hello4" class="com.huang.pojo.Hello" c:str="hhhh"></bean>
bean的作用域
- singleton(单例模式):Spring默认机制
- prototype(原型模式):每次从容器中get的时候都会产生一个新对象
- 其余request、session、application,在web开发中才能用
范围使用描述
| singleton(单例) | scope=“singleton” | (默认)为每个 Spring IoC 容器的单个 object 实例定义单个 bean 定义。 |
| prototype(原型) | scope=“prototype” | 为任意数量的 object 实例定义单个 bean 定义。 |
| request(请求) | scope=“request” | 将单个 bean 定义范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 bean 实例,该实例是在单个 bean 定义的后面创建的。仅在 web-aware Spring ApplicationContext的 context 中有效。 |
| session | scope=“session” | 将单个 bean 定义范围限定为 HTTP Session的生命周期。仅在 web-aware Spring ApplicationContext的 context 中有效。 |
| application(应用) | | 将单个 bean 定义范围限定为ServletContext的生命周期。仅在 web-aware Spring ApplicationContext的 context 中有效。 |
| WebSocket | | 将单个 bean 定义范围限定为WebSocket的生命周期。仅在 web-aware Spring ApplicationContext的 context 中有效。 |
bean 的自动装配(autowire)
使用注解开发
- 在使用Spring4之后,要使用注解开发,必须保证aop包导入了
- 使用注解要导入context约束,增加注解支持注解支持
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/>
<context:component-scan base-package="com.huang.pojo"/>
</beans>
bean(@Component)
- 加注解:@Component
- @Component:组件,放在类上,说明这个类被Spring管理了,就是bean
属性注入(@Value(""))
- 加注解:@Value("")
- 可以放在属性或者set方法上
- 适用简单属性,复杂属性适用xml注入
衍生的注解
- 【@Component】有几个衍生注解,在Web开发中,会按照mvc三层架构分层
- Dao:【@Repository】
- Service:【@Service】
- Controller:【@Controller】
- 这四个注解功能都一样 ,代表将这个类注册到Spring中,装配Bean
使用注解自动装配(@Autowired)
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.huang.Dao"/><context:annotation-config/>
</beans>
- 使用Autowired我们可以不用编写Set方法,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byName
- 显示的定义了@Autowired(required = false),说明这个对象可以为空,否则不允许为空
- @Nullable:字段标记了这个注解,说明这个注解可以为NULL
- @Qualifier(value=“指定的命名”):自动装配环境比较复杂的时候,无法通过一个注解完成时添加的
- 不使用Autowired时可以使用@Resource(name="")
@Autowired(required
= false)@Qualifier(value
= "hello2")private Hello hello
;
- @Autowired和@Resource的区别
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired通过byType的方式实现,而且必须要求这个对象存在
- @Resource默认通过byName方式实现,如果找不到名字,则通过byType实现
作用域
-
@Scope(“singleton”):单例模式
-
@Scope(“prototype”):原型模式
-
XML与注解
- xml:更加万能,适用于任何场合,维护简单方便
- 注解:不是自己的类适用不了,维护相对复杂
- xml与注解结合:
使用Java的方式配置Spring
- 不适用Spring的xml配置
- JavaConfig是Spring的一个子项目,Spring4之后变成了核心功能
写一个实体类
@Data
@Component
@Scope("singleton")
public class User {@Value("huang")private String name
;
}
写一个 配置类
@Configuration
@ComponentScan("com.huang")
@Import(config2
.class)
public class huangConfig {@Beanpublic User
getUser(){return new User();}
}
调用测试
@Testpublic void testHello(){ApplicationContext context
= new AnnotationConfigApplicationContext(huangConfig
.class);User user
= context
.getBean("getUser", User
.class);System
.out
.println(user
.toString());}
代理模式
静态代理
- 角色
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
- 客户角色:访问代理对象的人
- 代理模式的好处:
- 可以使用真实角色的操作更加纯粹,不用去关注一些公共业务
- 公共业务也就交给了代理角色!实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
- 缺点:
- 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低
- 动态代理好处:
- 一个动态代理代理的是一类接口,一般就是对应的一类业务
- 一个动态代理可以代理多个类,只要实现了同一个接口即可
动态代理
InvocationHandler是由代理实例的调用处理程序实现的接口。
每个代理实例都有一个关联的调用处理程序。 在代理实例上调用方法时,方法调用将被编码并调度到其调用处理程序的invoke方法。
public class ProxyInvocationHandler implements InvocationHandler {private Object target
;public void setTarget(Object target
) {this.target
= target
;}public Object
getProxy(){return Proxy
.newProxyInstance(this.getClass().getClassLoader(),target
.getClass().getInterfaces(),this);}public Object
invoke(Object proxy
, Method method
, Object
[] args
) throws Throwable
{Object result
= method
.invoke(target
, args
);return result
;}
}
- 测试使用:Host是一个继承Rent接口的实现类,Rent是一个
public class Client {public static void main(String
[] args
) {Host host
=new Host();ProxyInvocationHandler handler
= new ProxyInvocationHandler();handler
.setTarget(host
);Rent proxy
= (Rent
)handler
.getProxy(); proxy
.rent();}
}
AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
<dependency><groupId>org.aspectj
</groupId><artifactId>aspectjweaver
</artifactId><version>1.9.4
</version></dependency>
提供声明式事务:允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等
- 切面(ASPECT):横切关注点,被模块化的特殊对象。即,它是一个类
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法
- 目标(Target):被通知对象
- 代理(Proxy):向目标对象应用通知后创建的对象
- 切入点(PointCut):切面通知执行的“地点”的定义
- 连接点(JointPoint):与切入点匹配的执行点
使用Spring的API接口实现
- 创建两个日志类继承接口:MethodBeforeAdvice,AfterReturningAdvice
public class logBefore implements MethodBeforeAdvice {public void before(Method method
, Object
[] objects
, Object o
) throws Throwable
{System
.out
.println(o
.getClass().getName()+"的"+method
.getName()+"被执行了");}
}public class logAfter implements AfterReturningAdvice {public void afterReturning(Object o
, Method method
, Object
[] objects
, Object o1
) throws Throwable
{System
.out
.println("执行了"+method
.getName());}
}
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="host" class="com.Proxy.Host"/><bean id="logbefore" class="com.Proxy.Log.logBefore"/><bean id="logafter" class="com.Proxy.Log.logAfter"/><aop:config><aop:pointcut id="pointCut" expression="execution(* com.Proxy.Host.*(..))"/><aop:advisor advice-ref="logbefore" pointcut-ref="pointCut"/><aop:advisor advice-ref="logafter" pointcut-ref="pointCut"/></aop:config>
</beans>
@Testpublic void testAOP(){ApplicationContext context
= new ClassPathXmlApplicationContext("applicationContext.xml");Rent host
= (Rent
) context
.getBean("host");host
.rent();}
自定义切入点类
execution用法参考连接
public class DiyPointCut {private void before(){System
.out
.println("===============before=============");}private void after(){System
.out
.println("===============after=============");}
}
<bean id="diy" class="com.Proxy.Diy.DiyPointCut"/><aop:config><aop:aspect ref="diy"><aop:pointcut id="point" expression="execution(* com.Proxy.Host.*(..))"/><aop:before method="before" pointcut-ref="point"/><aop:after method="after" pointcut-ref="point"/></aop:aspect></aop:config>
注解实现AOP(@Aspect)
@Aspect
public class DiyPointCut {@Before("execution(* com.Proxy.Host.*(..))")private void before(){System
.out
.println("===============before=============");}@After("execution(* com.Proxy.Host.*(..))")private void after(){System
.out
.println("===============after=============");}@Around("execution(* com.Proxy.Host.*(..))")public void around(ProceedingJoinPoint jp
) throws Throwable
{System
.out
.println("===============around-before=============");Object proceed
= jp
.proceed(); System
.out
.println("===============around-after=============");}
}
<bean id="diy" class="com.Proxy.Diy.DiyPointCut"/><aop:aspectj-autoproxy/>
整合myBatis
官方文档
导入相关jar包 - junit
- myBatis
- mysql数据库
- Spring
- aop
- myBatis-spring
<dependencies><dependency><groupId>junit
</groupId><artifactId>junit
</artifactId><version>4.13
</version><scope>test
</scope></dependency><dependency><groupId>mysql
</groupId><artifactId>mysql-connector-java
</artifactId><version>5.1.49
</version></dependency><dependency><groupId>org.mybatis
</groupId><artifactId>mybatis
</artifactId><version>3.5.5
</version></dependency><dependency><groupId>org.springframework
</groupId><artifactId>spring-webmvc
</artifactId><version>5.2.8.RELEASE
</version></dependency><dependency><groupId>org.springframework
</groupId><artifactId>spring-jdbc
</artifactId><version>5.2.8.RELEASE
</version></dependency><dependency><groupId>org.aspectj
</groupId><artifactId>aspectjweaver
</artifactId><version>1.9.4
</version></dependency><dependency><groupId>org.mybatis
</groupId><artifactId>mybatis-spring
</artifactId><version>2.0.5
</version></dependency></dependencies>
编写数据源sqlSessionFactorysqlSessionTemplate给接口加实现类将自己写的实现类注入到mybatis测试
方法一
@Data
public class User {int id
;String class_name
;
}
public interface UserMapper {public List
<User> selectUser();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huang.mapper.UserMapper"><select id="selectUser" resultType="com.huang.pojo.User">select * from school.test2
</select>
</mapper>
@Data
public class UserMapperImpl implements UserMapper {SqlSessionTemplate sqlSession
;public List
<User> selectUser() {UserMapper mapper
= sqlSession
.getMapper(UserMapper
.class);return mapper
.selectUser();}
}
- myBatis-config.xml:mybatis的核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
- spring-dao.xml:整合配置文件,专注于mybatis的一些对象生成
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="123456"/></bean><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="configLocation" value="classpath:myBatis-config.xml"/><property name="mapperLocations" value="classpath:com/huang/mapper/UserMapper.xml"/></bean><bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"><constructor-arg index="0" ref="sqlSessionFactory"/></bean></beans>
- applicationContext.xml:总配置文件,引入spring-dao.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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><import resource="spring-dao.xml"/><bean id="userMapper" class="com.huang.mapper.UserMapperImpl"><property name="sqlSession" ref="sqlSession"/></bean><bean id="userMapper2" class="com.huang.mapper.UserMapperImpl2"><property name="sqlSessionFactory" ref="sqlSessionFactory"/></bean>
</beans>
@Testpublic void testSB(){ApplicationContext context
= new ClassPathXmlApplicationContext("applicationContext.xml");UserMapper userMapper
= context
.getBean("userMapper", UserMapper
.class);for (User user
: userMapper
.selectUser()) {System
.out
.println(user
.toString());}}
方法二
- 不用创建sqlsession对象了,实现接口继承一个SqlSessionDaoSupport对象
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {public List
<User> selectUser() {return getSqlSession().getMapper(UserMapper
.class).selectUser();}
}
<bean id="userMapper2" class="com.huang.mapper.UserMapperImpl2"><property name="sqlSessionFactory" ref="sqlSessionFactory"/></bean>
事务
- 事务的ACID原则:原子性,一致性,隔离性,持久性
声明式事务
-
使用AOP的方式
-
修改后的spring-dao.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
:aop
="http://www.springframework.org/schema/aop"xmlns
:tx
="http://www.springframework.org/schema/tx"xsi
:schemaLocation
="http
://www
.springframework
.org
/schema
/beanshttp
://www
.springframework
.org
/schema
/beans
/spring
-beans
.xsdhttp
://www
.springframework
.org
/schema
/aophttp
://www
.springframework
.org
/schema
/aop
/spring
-aop
.xsdhttp
://www
.springframework
.org
/schema
/txhttp
://www
.springframework
.org
/schema
/tx
/spring
-tx
.xsd"
><!--DataSource:使用spring的数据源替换mybatis的配置,使用spring提供的jdbc
--><bean id
="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name
="driverClassName" value
="com.mysql.jdbc.Driver"/><property name
="url" value
="jdbc:mysql://localhost:3306/school?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/><property name
="username" value
="root"/><property name
="password" value
="123456"/></bean
><!--sqlSessionFactory
--><bean id
="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name
="dataSource" ref
="dataSource"/><!--绑定myBatis
--><property name
="configLocation" value
="classpath:myBatis-config.xml"/><property name
="mapperLocations" value
="classpath:com/huang/mapper/UserMapper.xml"/></bean
><!--SqlSessionTemplate就是我们用的sqlSession
--><bean id
="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"><!--只能使用构造器注入,因为没有set函数
--><constructor
-arg index
="0" ref
="sqlSessionFactory"/></bean
><!--配置声明式事务
--><bean id
="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name
="dataSource" ref
="dataSource"/></bean
>
<!--结合AOP实现事务的织入
--><!--配置事务的通知
--><tx
:advice id
="txAdvice" transaction
-manager
="transactionManager"><!--给哪些方法配置事务
--><!--配置事务传播特性
--><tx
:attributes
><tx
:method name
="*" propagation
="REQUIRED"/></tx
:attributes
></tx
:advice
><!--配置事务切入
--><aop
:config
><aop
:pointcut id
="txPointCut" expression
="execution(* com.huang.mapper.*.*(..))"/><aop
:advisor advice
-ref
="txAdvice" pointcut
-ref
="txPointCut"/></aop
:config
></beans
>
总结
以上是生活随笔为你收集整理的SpringFramework-IOC(依赖注入)+AOP(面向切面编程)的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。