Tuesday, January 31, 2017

PowerMock简介

学习完Mockito以后,现在学习PowerMock。Mockito有局限性:不能mock私有方法、构造器、静态方法,equals,hashcode。PowerMock的出现正可以解决这一问题,它采用修改字节的方法做到的。

例如要mock一个Util的静态方法getHalfLegs,如果输入参数是很简单时候,当然直接真实调用getHalfLegs即可。但如果输入是一个复杂的类Animal,而Animal就是一个mock,这时就需要mock此方法的输出了。


@RunWith(PowerMockRunner.class) 
@PrepareForTest(Util.class) 
public class VideoSpeechletStaticTest {

@Before
public void setup(){
PowerMockito.mockStatic(Util.class);
}

@Test
public void test_PlusOneLeg(){

// Arrange
when(Util.getHalfLegs(animal)).thenReturn(10);

// Act
int actual = underTest.plusOneLeg(animal);

// Assert
assertEquals(11, actual);
}

@InjectMocks
VideoSpeechlet underTest;

@Mock
Animal animal;
}

这三个粗体地方都要注意@PrepareForTest可以指定多个要mock的含静态方法的类。PowerMock用到两个lib:
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.6.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.6.4</version>
    <scope>test</scope>
</dependency>

一定要注意版本一致,否则会出现奇怪错误。

ref:
https://blog.codecentric.de/en/2016/03/junit-testing-using-mockito-powermock/

Monday, January 30, 2017

Java代码缺陷自动分析工具 - CheckStyle/FindBugs/JaCoCo

1. CheckStyle

规定编码规范或叫code style。它规定了代码的样式问题,例如不能超过80列等。它让code style检查变得非常容易且规则模块化。如规定不允许start import只要加入自定义的checkstyle.xml中即可。

<!-- import中避免星号"*" -->
<module name="AvoidStarImport" />  

2. FindBugs

FindBugs 是一个java bytecode 静态分析工具,它可以帮助java 工程师提高代码质量以及排除隐含的缺陷。35种检查包括命名检查(名字不过长),未使用的代码检查。它是Eclipse一个插件。


3. JaCoCo

Java Code Coverage是一种分析单元测试覆盖率的工具,使用它运行单元测试后,可以给出代码中哪些部分被单元测试测到,哪些部分没有没测到,并且给出整个项目的单元测试覆盖情况百分比。EclEmma 是基于 JaCoCo 的一个 Eclipse 插件。有两个概念line coverage和branch coverage。前者是哪几行代码运行了,后者是遇到ANDs & ORs时候如if (a==2 && b==3)是否执行到。

为了提高coverage,可以对输入参数做mock,改变程序的路劲,让它多执行一些语句即可。

4. Velocity coverage

给出coverage报告和阈值


ref:
checkstyle
FindBugs
JaCoCo
Velocity

Java 依赖注入标准(JSR-330)CDI

JSR 330只是一种对DI的描述规范,就是用同一个的annotation来表示依赖,但具体实现由Spring或Guice来决定。

@Inject instead of Spring’s @Autowired to inject a bean.
@Named instead of Spring’s @Component to declare a bean.
@PostConstruct 类似于serverlet的init,bean产生->依赖注入->PostConstruct->service启动

@Named
public class Customer {
}

public class VideoApp {
@Inject
private Customer customer;

  @PostConstruct
  private void init(){
     System.out.println("postconstruct");
  }

}

Spring实现可以参考:
https://www.mkyong.com/spring3/spring-3-and-jsr-330-inject-and-named-example/


Spring的依赖注入


加入@Autowired后可以自动装配getter,setter就不用再写这些方法。
public class Zoo
{
    @Autowired
    private Tiger tiger;
 
    @Autowired
    private Monkey monkey;
 
    public String toString(){}
}

而Spring设置中只要加入<context:component-scan base-package="com.vtasters" />就会自动扫描vtasters这个包下所有注解,这样就不用在配置文件中声明bean了。

Guice的依赖注入

Guice完全用了Java依赖注入标准,也就是用了@Named, @Inject

ref:
Spring的注解
http://blog.csdn.net/DL88250/article/details/4838803#_Qualifier_9535574146353466_06

Sunday, January 29, 2017

JUnit简介 annotation

@BeforeClass和@AfterClass对一个test.java文件只跑一次,所以这两个方法都是static,方法名可以自定义。
@Before和@After对同一个test.java每个test的方法都会跑一次
@Test就是真正测试的方法,这里测试二分法
@RunWith 用在几个class一起测试或者用到其他框架。


@RunWith(Suite.class)
@SuiteClasses({ FirstDayAtSchoolTest.class, FirstDayAtSchoolTest2.class })
public class AllTests {
}
或者
@RunWith(MockitoJUnitRunner.class)

public class UtilTest {

// (static) Run once, e.g. Database connection, connection pool
    @BeforeClass
    public static void runOnceBeforeClass() {
        System.out.println("@BeforeClass - runOnceBeforeClass");
    }

    // e.g. Creating an similar object and share for all @Test
    @Before
    public void runBeforeTestMethod() {
        System.out.println("@Before - runBeforeTestMethod");
    }

    @Test
    public void test_BinarySearch() {
    // Arrange
    int a[] = new int[]{1,2,3,5,6,8,9};
   
    // Act
    int r = Util.indexOf(a, 3);
   
    // Assert
    assertEquals(2, r);
    }

    @Test
    public void test_BinarySearch_notFound() {
    // Arrange
int b[] = new int[]{1,2,3,3,3,5,6,8,9};

// Act
int r = Util.indexOf(b, 4);

// Assert
    assertEquals(-1, r);
    }
 
    // e.g. Closing an similar object and share for all @Test
    @After
    public void runAfterTestMethod() {
        System.out.println("@@After - runAfterTestMethod");
    }

    // (static) Run once, e.g. close database connection, connection pool
    @AfterClass
    public static void runOnceAfterClass() {
        System.out.println("@@AfterClass - runOnceAfterClass");
    }
}

Eclipse配置注释和代码格式

新建一个文件时,往往想加入作者注释:

Comments generation is configured in:

Main Menu -> Window -> Preferences -> Java -> Code Style -> Code Templates -> Configure generated code and comments

You can change directly the New Java files template: Code -> New Java files -> Edit


















常用有3个:
1. 全选所有代码右击->Source->Format
美观化如消除多余回车、正确缩进、上括号的位置(同一行还是新一行)

2. 全选所有代码右击->Source->Organize imports
删除unused imports和将start import转化

3. 全选所有代码右击->Source->Cleanup
删除unused imports加入@Override等等具体参看说明


Maven简介

Maven是一个自动下载和管理jar包或者叫dependency的工具,是Ant的进化版。自动下载只要填好Group id, artifact id以及version即可自动下载,不用手动下载和加入项目。而管理则体现在它建立一个叫.m2的jar包repository(如C:\Users\KK\.m2\repository),所有项目的dependency都从这里引入,从而做到jar包的重用,方便统一管理。

1. 安装且转化Maven项目

Maven的Eclipse安装在这里
创建一个POM文件可用默认






















2. 加入dependency

右击项目->Maven->Add dependency






如我要安装autovalue,Enter groupId搜索google,或者上网搜索jar包名+Maven找到id。
























按ok后就会自动下载到Maven Dependencies




















window->show view->other->Maven还可以看到所有项目的dependency








3. 删除dependency

可以到POM删除:










还可以通过右击POM->Open with->Text Editor来编辑

4. 转化folder structure

如果新建maven项目,会有src/main和src/test两个文件夹,现有converted to maven project就没有,只要编辑build path即可:


Wednesday, January 11, 2017

Java 8

Java 8的三大特性:Stream API, Optional API, 跟CompletableFuture稱為Java8三神器。
还会介绍apache commons的validate。

lambda

import java.util.stream.Collectors;

List<String> lines = Arrays.asList("spring", "node", "mkyong");

记得filter后要赋值
List<String> result = lines.stream() //convert list to stream
.filter(line -> !"mkyong". equals (line)) //filters the line, equals to "mkyong"
.collect(Collectors.toList()); //collect the output and convert streams to a List

result.forEach(System.out::println); //output : spring node

List<String> result;
result.forEach(x->func(x.getName()));

返回不是特定字符串的结果:
    String x = list.stream()
                   .filter(str->!StringUtils.equals(getSomething(str), "abc"))
                   .findAny()
                   .map(v -> getSomething(v))
                   .orElse(null);


map映射,可以将某一个类型的list转换成另一类型的list
List<Customer> customers = accounts.stream()
.filter(account -> account.getName()!="aaa")
.filter(account -> account.getAccountNumber()=="12")
.map(account ->{
Customer cust = new Customer();
cust.setCustomerId(Integer.parseInt(account.getAccountNumber()));
cust.setAddress(new Address("NEW", account.getName()));
return cust;
})
.collect(Collectors.toList());

字符串是否含有给定数组字符的一个:
Arrays.stream(items).parallel().anyMatch(inputStr::contains);

boolean ans = list.stream().anyMatch(n->n*(n+1))/4==5);

HashMap key排序
        Map<String, String> sortedMap = map.entrySet().stream() .sorted(Map.Entry.comparingByValue((String s1, String s2)-> s2.length() - s1.length())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); System.out.println("Sorted Map: " + Arrays.toString(sortedMap.entrySet().toArray()));

groupingBy统计数量

List<String> items =
                Arrays.asList("apple", "apple", "banana",
                        "apple", "orange", "banana", "papaya");

        Map<String, Long> result =
                items.stream().collect(
                        Collectors.groupingBy(
                                Function.identity(), Collectors.counting()
                        )
                );

Optonal

Optional的引入是为了解决null既可作为不存在或空结果的含糊性。有了Optional.isPresent的接口后,就可以让开发者意识到如何只处理空结果的情况,不用在考虑不存在情况。这个例子是找到第一个以L开头的字符串,若找到将其全大写输出。longest是一个Optional类型可以为空结果,若为空结果时,不会打印任何东西。

public void testOptional(){
Stream<String> names = Stream.of("aamurudu", "Okanbi", "Oduduwa");
Optional<String> longest = names
                               .filter(name -> name.startsWith("L"))
                               .findFirst();

longest.ifPresent(name -> {
           String s = name.toUpperCase();
           System.out.println("The longest name is "+ s);
       });
}

一般object变Optional:
Optional<Role> roleOpt = Optional.ofNullable(role); role可以为空,至于Optional.of("h")比如不为null。

Future

这个程序分成5个异步任务,等待5个任务(每个任务产生一个随机等待时间)全部完成后再输出完成信息。Future用于异步任务完成后执行主线程,它来保存异步任务结果,Future<Integer>参数类型就是返回类型。线程池设为10,以免线程不收回。tasks.add用了lambda表达式,类似于匿名类,简化程序。

public class AsyncTask {

public void asyncReturns() throws InterruptedException {
long startTime = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(10);

List<Callable<Integer>> tasks = new ArrayList<>();

// Build parallel tasks
for (int i = 0; i < 5; i++) {
tasks.add(() -> getRandomByRange(waitTime));
}

// Execute all calls at the same time
List<Future<Integer>> futures = executor.invokeAll(tasks);

for (int i = 0; i < futures.size(); i++) {
try {
Future<Integer> future = futures.get(i);
Integer result = future.get();
System.out.println("task " + i + " takes " + result + " millisec");
} catch (ExecutionException e) {
e.printStackTrace();
}

}
executor.shutdown();
long endTime = System.currentTimeMillis();
System.out.println("done in "+(endTime-startTime)+" millisec");
}

public int getRandomByRange(int upper) throws InterruptedException {
int wait = new Random().nextInt(upper) * 1000;
Thread.sleep(wait);
return wait;
}

int waitTime = 5;

}

假设执行没有返回值的异步任务,一定要shutdown,作用是执行提交任务且终止线程池,否则一直等待。Future的意思就是未来会完成的结果。
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(()-> getRandomByRange2());
  executor.submit(()-> getRandomByRange2());
executor.shutdown();

Runnable的lambda表达式

原有:
Runnable task1 = new Runnable(){
     @Override
    public void run(){
        System.out.println("Task #1 is running");
    }
};
Thread thread1 = new Thread(task1);
thread1.start();

新的:
Runnable task2 = () -> { System.out.println("Task #2 is running"); };
new Thread(task2).start();

可见lambda表达式简化了很多。


lang3

lang3-> notNull: 查某个参数是否null,如果是出现exception:java.lang.NullPointerException: The validated object is null。但参数可以为空。

声明
test(@Nullable final String abc)

验证
notNull(para, "para must be not null");
isTrue(para>0, "para must be greater than 0")

检查一个Collection类型(ArrayList, HashSet)是否空(null或无元素)
CollectionUtils.isEmpty(mylist)
StringUtils.isEmpty("")

数组
asList固定大小不支持加删,但支持改。singletonList大小为1,不支持加删改
List<String> aList = Arrays.asList("a", "b");
List<String> bList = Collections.singletonList("a");

首字母大写
StringUtils.capitalized()

不可改变Map, Google Guava (Google Collections Library)
ImmutableSet<String> COLOR_NAMES = ImmutableSet.of("Red")
Map<String, String> COLOR_Map = ImmutableMap.of("1","Red")

类模板
 @Override
 public boolean equals(Object other)
 {
     return EqualsBuilder.reflectionEquals(this, other);
 }

  @Override
 public String toString()
 {
     return ToStringBuilder.reflectionToString(this);
 }

findFirst vs findAny
区别只在于多线程情况下,findFirst仍能保证返回data stream的第一个元素。


ref:
lambda
optional

Monday, January 9, 2017

LESS -- 动态的CSS语言

LESS 将 CSS 赋予了动态语言的特性,如 变量, 继承, 运算, 函数。

变量:
@color: #4D926F;

#header {
  color: @color;
}
h2 {
  color: @color;
}

继承(#header)和函数(#footer):
.rounded-corners (@radius: 5px) {
  border-radius: @radius;
  -webkit-border-radius: @radius;
  -moz-border-radius: @radius;
}

#header {
  .rounded-corners;
}
#footer {
  .rounded-corners(10px);
}

嵌套:
p { font-size: 12px;
    a { text-decoration: none;
      &:hover { border-width: 1px }
    }
  }

函数 & 运算:
@base-color: #111;
@red:        #842210;

#footer {
  color: @base-color + #003300;
  border-color: desaturate(@red, 10%);
}

ref:
中文参考


require.js -- js文件模块化、依赖和异步加载

为了适应后端编程,所以引入模块化。其作用还在于加快网页加载速度

ref:
中文介绍

Backbone.js -- JavaScript的以Model驱动自动页面更新

在Backbone.js有几个重要的概念:
Model是根据现实数据建立的抽象,比如人(People)
Collection是Model的一个集合,比如一群人
View是对Model和Collection中数据的展示,把数据渲染(Render)到页面上
Router是对路由的处理,就像传统网站通过url实现不同的页面

通过Backbone,你可以把你的数据当作Models,通过Models你可以创建数据,进行数据验证,销毁或者保存到服务器上。当界面上的操作引起model中属性的变化时,model会触发change的事件。那些用来显示model状态的views会接受到model触发change的消息,进而发出对应的响应,并且重新渲染新的数据到界面。在一个完整的Backbone应用中,你不需要写事件驱动代码来从DOM中通过特殊的id来获取节点手工地更新页面,因为在model发生变化时,views会很简单的进行自我更新。

ref:
中文介绍

AWS S3

云存储服务,是object storage的服务。

使用场景
1. 大数据存储(如交易数据替代FTP)
2. 数据backup
3. 网站静态数据
4. 网站视频存储

S3是key-value存储,value大小从0到5TB,所以其实也是ElastiCache的更便宜的替代方案。S3比DynamoDB数据访问较慢,因为没有primary key和index。

架构
存储桶
* 创建存储桶之后,任何 AWS 区域中的其他 AWS 账户均不能使用该存储桶的名称
* 每个 AWS 账户中创建多达 100 个存储桶
* 一个存储桶中可以存储任意数量的对象
* 存储桶中存储所有对象时对这些对象进行加密
* S3 Transfer Acceleration 可在客户端与 S3 存储桶之间实现快速、轻松、安全的远距离文件传输。利用 Amazon CloudFront 的全球分布式边缘站点
* 分离存储和数据转移费用。申请方付款存储桶:申请方将支付请求和从存储桶下载数据的费用。
* 每个存储桶都具有关联的访问控制策略

对象
* 键 – 分配给对象的名称。您可以使用对象键检索该对象。
* 版本 ID – 在存储桶中,键和版本 ID 唯一地标识对象。
* 对象值可以是任意序列的字节。对象的大小范围是 0 到 5 TB。
* 元数据 – 一组名称值对,可用于存储有关对象的信息。
* 子资源 – Amazon S3 使用子资源机制存储特定于对象的其他信息。
因为子资源从属于对象,因此它们始终与某些其他实体 (如对象或存储桶) 相关联。
* 访问控制信息 – 您可以控制对您在 Amazon S3 中存储的对象的访问。
Amazon S3 支持基于资源的访问控制(如访问控制列表 (ACL) 和存储桶策略)和基于用户的访问控制。

创建对象时,要指定键名,它在存储桶中唯一地标识该对象。不存在子存储桶或子文件夹的层级结构。
但您可以使用键名前缀和分隔符推断逻辑层次结构
元数据包括上次修改日期,内容长度和用户定义的对象元数据(“x-amz-meta-”开头)等。
版本控制:

最佳实践
跨账号访问文件:owner账号上的bucket->permissions->Access Control List上add account加入外部账号的Canonical ID (这个ID可以在该账号的某一个bucket的permission上找到)。这样只要登录外部账号后,用https://s3.console.aws.amazon.com/s3/buckets/mybucket即可访问。

注意用外部账号上传文件,这个文件默认owner是外部账号,bucket owner账号是没有访问权限的。所以上传文件是要Add account加入bucket owner的Canonical ID

功能
Amazon S3 存储类
元数据x-amz-storage-class 请求指定存储类

存储类设计专门针对持久性 (设计目标)可用性 (设计目标)可用区最小存储持续时间最小可计费对象大小其他考虑因素
STANDARD
经常访问的数据
99.999999999%
99.99%
>=3
STANDARD_IA
长时间存在的、不经常访问的数据
99.999999999%
99.9%
>=3
30 天
128 KB
按每 GB 收取检索费用。
INTELLIGENT_TIERING
访问模式发生变化或未知的长时间存在的数据
99.999999999%
99.9%
>=3
30 天
按对象收取监控和自动化费用。无检索费用。
ONEZONE_IA
长时间存在的、不经常访问的、非关键数据
99.999999999%
99.5%
1
30 天
128 KB
按每 GB 收取检索费用。无法灵活地应对可用区丢失的情况。
GLACIER
检索时间从数分钟到数小时不等的长期数据存档99.999999999%
99.99% (在您还原对象之后)
>=3
90 天
按每 GB 收取检索费用。您必须先还原存档对象,然后才可以访问它们。有关更多信息,请参阅 还原存档对象
DEEP_ARCHIVE
存档很少访问的数据,默认检索时间为 12 小时99.999999999%
99.99% (在您还原对象之后)
>=3
180 天
按每 GB 收取检索费用。您必须先还原存档对象,然后才可以访问它们。有关更多信息,请参阅 还原存档对象
RRS(不推荐)
经常访问的非关键数据
99.99%
99.99%
>=3
GET请求将检索最新存储的版本。在当前版本为删除标记时,执行简单 GET Object 请求将返回 404 Not Found 错误(soft delete)

对象生命周期管理
定义对象转换为另一个存储类的时间,如创建 30 天后将其转换为 STANDARD_IA 存储类
过期操作 — 定义对象的过期时间。Amazon S3 将代表您异步删除过期的对象

CORS
假设您在名为website的Amazon S3存储桶中托管网站(如在 Amazon S3 上托管静态网站中所述)。您的用户加载了网站终端节点 http://website.s3-website-us-east-1.amazonaws.com。现在,您想要使用此存储桶中存储的网页上的 JavaScript

分段上传
在单个操作中上传对象 — 借助单个 PUT 操作,您可以上传多达 5 GB 的对象
分段上传对象:多线程上传和断点上传

* 复制对象
* 分段上传 API 复制对象
* 列出包含在存储桶中的键,使用带列表操作的Prefix作为条件
* 删除对象,删除多个对象
* 清单是S3提供的用于帮助管理您的存储的工具之一。使用它来审核和报告对象的复制和加密状态
* 从对象中选择内容。利用Amazon S3 Select,用SQL筛选S3对象的内容。适用于以CSV、JSON或Apache Parquet格式存储的对象。
* Amazon S3 object lock。使用一次写入,多次读取 (WORM) 模式存储对象。您可以​使用它在固定的时间段内或无限期地阻止删除或覆盖对象。仅适用于受版本控制的存储桶
* 执行批量操作如对象复制,启动还原对象,调用Lambda函数
* 托管静态网站
* 通知功能如新对象创建事件,对象删除事件,目标为SNS,SQS, Lambda
* 跨区域复制
* BitTorrent。P2P协议,通过将正在下载对象的客户端用作分发服务器来解决此问题

底层API
getObject
fullObject = s3Client.getObject(new GetObjectRequest(bucketName, key));

putObject
// Upload a text string as a new object.
s3Client.putObject(bucketName, stringObjKeyName, "Uploaded String Object");
         
// Upload a file as a new object with ContentType and title specified.
PutObjectRequest request = new PutObjectRequest(bucketName, fileObjKeyName, new File(fileName));
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType("plain/text");
metadata.addUserMetadata("x-amz-meta-title", "someTitle");
request.setMetadata(metadata);
s3Client.putObject(request);


其他
S3的Pyton库是Boto3

如果lambda用了default的security group,同样地S3也要设VPC,否则lambda就不能访问S3。通过new VPC endpoint for S3就可以设置。还要在IAM加入S3FullAccess policy到lambda role中,这样在python代码中不需要secret key可以直接访问s3 = boto3.client('s3')

AWS以外要访问S3可以通过创建secret key来实现。


Ref
官网


Amazon ElastiCache

Amazon ElastiCache是建于Redis或Memcached(可选)上的AWS产品。它存数据与memory,所以价钱比S3和DynamoDB都要贵。

Google AutoValue

在自定义类型中如

public class Money {
  public Currency currency;
  public long amount;
}

我们希望currency和amount相等的情况下,Money应该一样,且Money不能变。这时,我们要写很多代码去实现hashCode和equals以及toString,还要声明currency和amount为final。这是重复性工作,AutoValue可以帮你自动生成这些代码,使用如下:

@AutoValue
public abstract class Animal {
  abstract String name();
  abstract int numberOfLegs();
  @Nullable
  abstract String category();
  private String derivedName;
 
  public static Builder builder() {
    return new AutoValue_Animal.Builder();
  }

  public final String drivedName(){
 return category() + ":" + name();
  }

  @AutoValue.Builder
  public abstract static class Builder {
    public abstract Builder setName(String value);
    public abstract Builder setNumberOfLegs(int value);
    public abstract Animal build();
  }
}

AutoValue只有在新建类的时候设定值,之后不能再改变,只能get,这叫immutable object。category设置nullable可以不出现在builder中,否则会报错。derived域不用abstract。

Animal dog = Animal.builder().setName("dog").setNumberOfLegs(4).build();
Animal cat = Animal.builder().setName("cat").setNumberOfLegs(4).setCategory("mammal").build();


Eclipse设置

这个用注解生成代码的方法叫annotation processing. 需要在eclipse中做改动:
1. 有4个jar加入到Java Build path->Libraries中:
      auto-service-1.0-rc1.jar
      guava-16.0.1.jar
      jsr305-2.0.3.jar
      auto-value-1.1.jar (不能用1.0版本)
2. enable annotation processing表示生成java文件的文件夹叫generated(任意名)
3. Factory path中加入4个jar包
4. 把generated文件夹加入到Java Build Path->Source中,项目就可以加入自动生成java一起编译
这样写代码(Animal.java)保存可生成AutoValue_Animal.java

















ref:
Official website
原理讲解

SQL Injection



SQL注入攻击是通过数据库漏洞获取有用数据










' or 1=1--

如有些网站程序的SQL就变成:
select * from users where name='test' and password='' or 1=1--'


所以要做到:
1. 验证输入
2. 加密输出output比如log

Friday, January 6, 2017

Bookmarklet

Bookmarklet是一个javascript applet,或者叫bookmark applet。可以
1. 用于获取HTML信息如productId
2. 修改网页外观
3. 加入嵌入功能(如显示加入公司时间功能)

原理是bookmark实际是保存http://, ftp://或javascript:,bookmarklet就用到javascript协议,嵌入javascript代码实现。更高级自动工具见Greasemonkey,它可实现自动运行script,从而无缝引入额外功能(如比价功能,加入公司时间功能)

bookmarklet按以下步骤设置:
URL输入
javascript:var sku=document.getElementById("product_store_sku").innerText;var output=prompt("sku is",sku);if(output){} else{};

打开网页任一HomeDepot产品详情页面
再点击新创建的bookmark


Tuesday, January 3, 2017

Spring简介 -- 基本结构和注解

基本Spring含有beans.xml配置文件以及Main.java(任意含main的java文件)启动
在com/resources/com/vtasters/beans.xml。用到spring-context包。

最简单的例子

beans.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/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="video" class="com.vtasters.Video">
      </bean>
    </beans>

Main.java:
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/vtasters/beans.xml");
Video t = (Video) context.getBean("video");
   }      
}


注解:

Spring引入Java依赖注入标准后,使得beans.xml更简洁。Spring会扫描包里面的每个类,@Named就是注册bean,省去beans.xml的定义。

beans.xml加入注解和扫描包:
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"

<context:component-scan base-package="com.vtasters" />

@Inject会自动注入实例,不用再手动产生,立刻可以使用(虽然为空)。

public class Video {
@Inject
private Customer customer;
}

@Named
public class Customer {}


ref:
Spring helloworld项目
如何加入beans.xml
注解扫描

其他讲解:

Spring主要用于Dependency Injection,它将实例的创建独立出来XML或config class。所以如果java代码没有contructor定义(特别时带参数的),就要到Beans.xml中找。

下面重点解释Beans.xml中每个设置:

id

是整个服务的唯一标示(用户定义)

Scope:

用于定义实例是否单例模式。值为singleton或prototype(每次产生新实例)

HelloWorld.java:
public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }

   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
}

Beans.xml:
  <bean id="helloWorld" class="com.tutorialspoint.HelloWorld" scope="singleton"/>

Main.xml:
 HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
 objA.setMessage("I'm object A");
 objA.getMessage();
 HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
 objB.getMessage();

每次返回一个结果。

constructor-arg:

public class Foo {
   public Foo(Bar bar, Baz baz) {}
}

 <bean id="foo" class="com.example.Foo">
      <constructor-arg ref="bar"/>
      <constructor-arg ref="baz"/>
   </bean>
   <bean id="bar" class="com.example.Bar"/>
   <bean id="baz" class="com.example.Baz"/>
这是自定义class。如果constructor是primitive类型:
   <bean id="exampleBean" class="examples.ExampleBean">
      <constructor-arg type="int" value="2001"/>
      <constructor-arg type="java.lang.String" value="Zara"/>
   </bean>

property:

用于指定类变量及其初值
   <bean id="helloWorld" class="com.tutorialspoint.HelloWorld">
      <property name="message" value="Hello World!"/>
   </bean>

parent/abstract:

   <bean id="helloWorld" class="com.tutorialspoint.HelloWorld"/>
   <bean id="helloIndia" class="com.tutorialspoint.HelloIndia" parent="helloWorld"/>
如果父类是抽象类,加入abstract即可
   <bean id="beanTeamplate" abstract="true"/>

inner bean:

public class TextEditor {
   private SpellChecker spellChecker;
}

 <bean id="textEditor" class="com.tutorialspoint.TextEditor">
      <property name="spellChecker">
         <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"/>
       </property>
   </bean>

Collection Injection:

     <property name="addressList">
         <list>
            <value>INDIA</value>
            <value>Pakistan</value>
            <value>USA</value>
            <value>USA</value>
         </list>
      </property>


Annotation Based Configuration

上述是以xml为基础的config,下面介绍以Annotation为基础,更为简洁和易读。

@Autowired在setter:

public class TextEditor {
   private SpellChecker spellChecker;

   @Autowired
   public void setSpellChecker( SpellChecker spellChecker ){
      this.spellChecker = spellChecker;
   }
   public SpellChecker getSpellChecker( ) {
      return spellChecker;
   }
   public void spellCheck() {
      spellChecker.checkSpelling();
   }
}

   <!-- 这里就不需要constructor-arg  -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor">
   </bean>


@Autowired在constructor:
  @Autowired
   public TextEditor(SpellChecker spellChecker){
      System.out.println("Inside TextEditor constructor." );
      this.spellChecker = spellChecker;
   }

   <!-- 这里就不需要constructor-arg  -->
 <bean id="textEditor" class="com.tutorialspoint.TextEditor">
 </bean>


@Autowired在property:

   这样就不需要setter了
  @Autowired

   private SpellChecker spellChecker;

Monday, January 2, 2017

控制反转---依赖注入和Service Locator pattern


控制反转(Inversion of Control, IoC)

public class Client {
    private Service service;
    Client() {
        service = new ServiceExample();
    }
}

Client中用到了Service的对象service,代码中显式的new一个Service的对象。采用依赖注入技术之后,Client的代码不需要直接new来获得这个对象,而是通过相关的容器控制程序来将Service对象在外部new出来并注入到Client类里的引用中。而Service对象被获取时的状态由配置文件(如XML)来指定。

Client(Service service) {
    this.service = service;
}


通过控制反转,对象在被创建的时候,由一个调控系统将其所依赖的对象(Service)注入到目前对象(Client)中。这种依赖(如初始化Service需要加入参数)会被独立出来成XML或一个class从而提高代码可读性和减低计算机代码之间的耦合度(隔离)方便单元测试。当然,一些不利因素就是要学习Spring。

Dependency injection

依赖注入是IoC最常见的实现方式。DI支持composition pattern(就是把另一个类作为此类的一个成员变量)而不太支持继承。

Construction Injection:
Client(Service service) {
    this.service = service;
}

Setter Injection:
public void setService(Service service) {
     this.service = service;
}

Interface Injection:
    @Override
    public void setService(Service service) {
        this.service = service;
    }

依赖注入代码实现:
Manually:
public class Injector {
    public static void main(String[] args) {
        Service service = new ServiceExample();
        Client client = new Client(service);

        System.out.println(client.greet());
    }
}

XML:
public static void main(String[] args) {
        BeanFactory beanfactory=new ClassPathXmlApplicationContext("Beans.xml");
       Client client=(Client)beanfactory.getBean("client");

System.out.println(client.greet());
}
<beans>
    <bean id="service" class="ServiceExample">
    </bean>
    <bean id="client" class="Client">
        <constructor-arg value="service" />      
    </bean>
</beans>

Config.java:
public class Injector {
    public static void main(String[] args) {
        BeanFactory beanfactory = new AnnotationConfigApplicationContext(MyConfiguration.class);
        Client client = beanfactory.getBean(Client.class);

        System.out.println(client.greet());
    }
}

@ComponentScan
static class MyConfiguration {
    @Bean
    public Client client(ServiceExample service) {
        return new Client(service);
    }
}

Service Locator pattern:

控制反转的另一种实现方式是Dependency lookup, 具体用Service Locator pattern来实现。此模式实质是工厂模式。
Locator提供各个接口如IServiceA, IServiceB, 以及一个_mapping的directory为接口和具体实现类做lookup,比如IServiceA->ServiceA。Locator还负责创建具体类的实例。

ServiceA:
interface IServiceA
{
      string GetData();
}

class ServiceA : IServiceA
{
      public string GetData()
      {
            return "This data is from ServiceA";
      }
}

一个新的服务就要先注册,注册实质上把这个实例类型放入hash中可以创建实例
Main:
 static void Main(string[] args)
 {
     //注册此类型
      ServiceLocator.RegisterType<IServiceA, ServiceA>();
     //创建此类型一个实例
      IServiceA serviceA = ServiceLocator.Resolve<IServiceA>();
      string data = serviceA.GetData();
      Console.WriteLine(data);
      Console.ReadKey();
 }